From 2c3e7e48fc6f4ea27cdb837af103a01faa0e9641 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sun, 16 Sep 2018 00:01:48 -0400 Subject: [PATCH] Fix major memory leaks in ExpiringMap computeIfAbsent would leak as the entry was never registered into the ttl map This fixes that ,as well as redesigns cleaning to not run on every manipulation, and instead to run clean once per tick per expiring map. --- ...ix-major-memory-leaks-in-ExpiringMap.patch | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Spigot-Server-Patches/0372-Fix-major-memory-leaks-in-ExpiringMap.patch diff --git a/Spigot-Server-Patches/0372-Fix-major-memory-leaks-in-ExpiringMap.patch b/Spigot-Server-Patches/0372-Fix-major-memory-leaks-in-ExpiringMap.patch new file mode 100644 index 0000000000..5e81011ffa --- /dev/null +++ b/Spigot-Server-Patches/0372-Fix-major-memory-leaks-in-ExpiringMap.patch @@ -0,0 +1,112 @@ +From c9297fde1beff53e303bb09caf0bb4b20c6792ce Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 16 Sep 2018 00:00:16 -0400 +Subject: [PATCH] Fix major memory leaks in ExpiringMap + +computeIfAbsent would leak as the entry +was never registered into the ttl map + +This fixes that ,as well as redesigns cleaning to +not run on every manipulation, and instead to run clean +once per tick per expiring map. + +diff --git a/src/main/java/net/minecraft/server/ExpiringMap.java b/src/main/java/net/minecraft/server/ExpiringMap.java +index 4006f5a69c..5345b97241 100644 +--- a/src/main/java/net/minecraft/server/ExpiringMap.java ++++ b/src/main/java/net/minecraft/server/ExpiringMap.java +@@ -6,25 +6,44 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + import it.unimi.dsi.fastutil.longs.Long2LongMap.Entry; + import it.unimi.dsi.fastutil.objects.ObjectIterator; + import java.util.Map; ++import java.util.function.LongFunction; + + public class ExpiringMap extends Long2ObjectOpenHashMap { + private final int a; +- private final Long2LongMap b = new Long2LongLinkedOpenHashMap(); ++ private final Long2LongMap ttl = new Long2LongLinkedOpenHashMap(); // Paper + + public ExpiringMap(int i, int j) { + super(i); + this.a = j; + } + +- private void a(long i) { +- long j = SystemUtils.b(); +- this.b.put(i, j); +- ObjectIterator objectiterator = this.b.long2LongEntrySet().iterator(); ++ // Paper start ++ private synchronized void setAccess(long i) { a(i); } // Paper - OBFHELPER ++ private synchronized void a(long i) { ++ long j = System.currentTimeMillis(); // Paper ++ this.ttl.put(i, j); ++ if (!registered) { ++ registered = true; ++ MinecraftServer.getServer().expiringMaps.add(this); ++ } ++ } ++ ++ @Override ++ public synchronized T computeIfAbsent(long l, LongFunction longFunction) { ++ setAccess(l); ++ return super.computeIfAbsent(l, longFunction); ++ } ++ private boolean registered = false; ++ ++ // Break clean to its own method to be ticked ++ synchronized boolean clean() { ++ long now = System.currentTimeMillis(); ++ ObjectIterator objectiterator = this.ttl.long2LongEntrySet().iterator(); // Paper + + while(objectiterator.hasNext()) { +- Entry entry = (Entry)objectiterator.next(); +- Object object = super.get(entry.getLongKey()); +- if (j - entry.getLongValue() <= (long)this.a) { ++ Long2LongMap.Entry entry = objectiterator.next(); // Paper ++ T object = super.get(entry.getLongKey()); // Paper ++ if (now - entry.getLongValue() <= (long)this.a) { + break; + } + +@@ -33,7 +52,18 @@ public class ExpiringMap extends Long2ObjectOpenHashMap { + objectiterator.remove(); + } + } +- ++ if (this.ttl.size() != this.size) { ++ MinecraftServer.LOGGER.error("WARNING: ExpiringMap desync - Memory leak risk!"); ++ for (Entry entry : this.entries) { ++ ttl.putIfAbsent(entry.getLongKey(), now); ++ } ++ } ++ if (isEmpty()) { ++ registered = false; ++ return true; ++ } ++ return false; ++ // Paper end + } + + protected boolean a(T var1) { +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 80e8b023cf..70a609efcc 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -155,6 +155,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati + public int autosavePeriod; + public File bukkitDataPackFolder; + public CommandDispatcher vanillaCommandDispatcher; ++ public List expiringMaps = java.util.Collections.synchronizedList(new java.util.ArrayList<>()); // PAper + // CraftBukkit end + // Spigot start + public static final int TPS = 20; +@@ -995,6 +996,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati + this.methodProfiler.e(); + org.spigotmc.WatchdogThread.tick(); // Spigot + PaperLightingQueue.processQueue(startTime); // Paper ++ expiringMaps.removeIf(ExpiringMap::clean); // Paper + this.slackActivityAccountant.tickEnded(l); // Spigot + co.aikar.timings.TimingsManager.FULL_SERVER_TICK.stopTiming(); // Paper + } +-- +2.19.0 +