100 lines
4.8 KiB
Diff
100 lines
4.8 KiB
Diff
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: Aikar <aikar@aikar.co>
|
||
|
Date: Sat, 23 May 2020 01:31:06 -0400
|
||
|
Subject: [PATCH] Fix Non Full Status Chunk NBT Memory Leak
|
||
|
|
||
|
Any full status chunk that was requested for any status less than full
|
||
|
would hold onto their entire nbt tree and every variable in that function.
|
||
|
|
||
|
This was due to use of a lambda that persists on the Chunk object
|
||
|
until that chunk reaches FULL status.
|
||
|
|
||
|
With introduction of no tick, we greatly increased the number of non
|
||
|
full chunks so this was really starting to hurt.
|
||
|
|
||
|
We further improve it by making a copy of the nbt tag with only the memory
|
||
|
it needs, so that we dont have to hold a copy to the entire compound.
|
||
|
|
||
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkRegionLoader.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkRegionLoader.java
|
||
|
index 91bcbf7156dd90b00e2d53bb6bff4abc44ecb721..8e4924cd649c350520cba54a0e1497d5acf089ff 100644
|
||
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkRegionLoader.java
|
||
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkRegionLoader.java
|
||
|
@@ -22,6 +22,7 @@ import net.minecraft.core.BlockPosition;
|
||
|
import net.minecraft.core.IRegistry;
|
||
|
import net.minecraft.core.RegistryBlocks;
|
||
|
import net.minecraft.core.SectionPosition;
|
||
|
+import net.minecraft.nbt.NBTBase;
|
||
|
import net.minecraft.nbt.NBTTagCompound;
|
||
|
import net.minecraft.nbt.NBTTagList;
|
||
|
import net.minecraft.nbt.NBTTagLongArray;
|
||
|
@@ -199,15 +200,9 @@ public class ChunkRegionLoader {
|
||
|
object2 = protochunkticklist1;
|
||
|
}
|
||
|
|
||
|
- object = new Chunk(worldserver.getMinecraftWorld(), chunkcoordintpair, biomestorage, chunkconverter, (TickList) object1, (TickList) object2, j, achunksection, (chunk) -> {
|
||
|
- loadEntities(nbttagcompound1, chunk);
|
||
|
- // CraftBukkit start - load chunk persistent data from nbt
|
||
|
- net.minecraft.nbt.NBTBase persistentBase = nbttagcompound1.get("ChunkBukkitValues");
|
||
|
- if (persistentBase instanceof NBTTagCompound) {
|
||
|
- chunk.persistentDataContainer.putAll((NBTTagCompound) persistentBase);
|
||
|
- }
|
||
|
- // CraftBukkit end
|
||
|
- });
|
||
|
+ object = new Chunk(worldserver.getMinecraftWorld(), chunkcoordintpair, biomestorage, chunkconverter, (TickList) object1, (TickList) object2, j, achunksection, // Paper start - fix massive nbt memory leak due to lambda. move lambda into a container method to not leak scope. Only clone needed NBT keys.
|
||
|
+ createLoadEntitiesConsumer(new SafeNBTCopy(nbttagcompound1, "TileEntities", "Entities", "ChunkBukkitValues")) // Paper - move CB Chunk PDC into here
|
||
|
+ );// Paper end
|
||
|
} else {
|
||
|
ProtoChunk protochunk = new ProtoChunk(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, worldserver); // Paper - Anti-Xray - Add parameter
|
||
|
|
||
|
@@ -313,6 +308,50 @@ public class ChunkRegionLoader {
|
||
|
return new InProgressChunkHolder(protochunk1, tasksToExecuteOnMain); // Paper - Async chunk loading
|
||
|
}
|
||
|
}
|
||
|
+ // Paper start
|
||
|
+
|
||
|
+ /**
|
||
|
+ * This wrapper will error out if any key is accessed that wasn't copied so we can catch it easy on an update
|
||
|
+ */
|
||
|
+ private static class SafeNBTCopy extends NBTTagCompound {
|
||
|
+ private final java.util.Set<String> keys = new java.util.HashSet<String>();
|
||
|
+ public SafeNBTCopy(NBTTagCompound base, String... keys) {
|
||
|
+ for (String key : keys) {
|
||
|
+ this.keys.add(key);
|
||
|
+ final NBTBase nbtBase = base.get(key);
|
||
|
+ if (nbtBase != null) {
|
||
|
+ this.set(key, nbtBase);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public boolean hasKey(String s) {
|
||
|
+ if (super.hasKey(s)) {
|
||
|
+ return true;
|
||
|
+ } else if (keys.contains(s)) {
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+ throw new IllegalStateException("Missing Key " + s + " in SafeNBTCopy");
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public boolean hasKeyOfType(String s, int i) {
|
||
|
+ return hasKey(s) && super.hasKeyOfType(s, i);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ private static java.util.function.Consumer<Chunk> createLoadEntitiesConsumer(NBTTagCompound nbt) {
|
||
|
+ return (chunk) -> {
|
||
|
+ loadEntities(nbt, chunk);
|
||
|
+ // CraftBukkit start - load chunk persistent data from nbt
|
||
|
+ NBTBase persistentBase = nbt.get("ChunkBukkitValues");
|
||
|
+ if (persistentBase instanceof NBTTagCompound) {
|
||
|
+ chunk.persistentDataContainer.putAll((NBTTagCompound) persistentBase);
|
||
|
+ }
|
||
|
+ // CraftBukkit end
|
||
|
+ };
|
||
|
+ }
|
||
|
+ // Paper end
|
||
|
|
||
|
// Paper start - async chunk save for unload
|
||
|
public static final class AsyncSaveData {
|