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 000000000..5e81011ff --- /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 +