diff --git a/Spigot-Server-Patches/0530-Fix-Non-Full-Status-Chunk-NBT-Memory-Leak.patch b/Spigot-Server-Patches/0530-Fix-Non-Full-Status-Chunk-NBT-Memory-Leak.patch new file mode 100644 index 0000000000..29b5e63ec6 --- /dev/null +++ b/Spigot-Server-Patches/0530-Fix-Non-Full-Status-Chunk-NBT-Memory-Leak.patch @@ -0,0 +1,72 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +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/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +index d752e793f13a9caf5d255293f7ce9d562fd50064..1685237dfd7d6f352c5bab5f65817462de5e6d12 100644 +--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java ++++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +@@ -157,9 +157,9 @@ public class ChunkRegionLoader { + object2 = protochunkticklist1; + } + +- object = new Chunk(worldserver.getMinecraftWorld(), chunkcoordintpair, biomestorage, chunkconverter, (TickList) object1, (TickList) object2, j, achunksection, (chunk) -> { +- loadEntities(nbttagcompound1, chunk); +- }); ++ 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")) ++ );// Paper end + } else { + ProtoChunk protochunk = new ProtoChunk(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, worldserver); // Paper - Anti-Xray - Add parameter + +@@ -265,6 +265,37 @@ 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 keys = new java.util.HashSet(); ++ public SafeNBTCopy(NBTTagCompound base, String... keys) { ++ for (String key : keys) { ++ this.keys.add(key); ++ this.set(key, base.get(key)); ++ } ++ } ++ ++ @Override ++ public boolean hasKey(String s) { ++ if (this.keys.contains(s)) { ++ return true; ++ } ++ 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 createLoadEntitiesConsumer(NBTTagCompound nbt) { ++ return (chunk) -> loadEntities(nbt, chunk); ++ } ++ // Paper end + + // Paper start - async chunk save for unload + public static final class AsyncSaveData {