首页 > 解决方案 > Java中的WeakHashMap如何泄漏内存?

问题描述

我正在运行一个大型 Minecraft(Spigot) 服务器,它带有一个专有的、严重混淆的 AntiCheat 插件以及我们定制的一组经过实战测试的插件。服务器多次创建和卸载新世界,因此在卸载、离开CraftPlayerCraftWorld对象后进行 GC。

我们正在使用 G1GC 和所谓的“aikar gc flags”在 Java 13(Java 12 也是如此)上运行

java -XX:+UseLargePagesInMetaspace -XX:+UseG1GC 
     -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=100 
     -XX:TargetSurvivorRatio=90 -XX:G1NewSizePercent=50 
     -XX:G1MaxNewSizePercent=80 -XX:G1MixedGCLiveThresholdPercent=35 
     -XX:+ParallelRefProcEnabled -jar Spigot.jar

AntiCheat 保持对CraftPlayers 的弱引用,但它们会泄漏,并且由于泄漏足够大,服务器的 CPU 会非常剧烈地旋转以最终与OutOfMemoryError. 手动运行System.gc()也不会清理这些。

当 AntiCheat 被禁用时,泄漏就消失了。

CraftWorlds堆转储中对所有泄漏的唯一 gc-root 引用CraftPlayer是 中的条目WeakHashMap,键是CraftPlayer。(CraftPlayerCraftWorld在正常 GCd 之前相互交叉引用)。

有一种方法可以通过 a 进行泄漏WeakHashMap:如果您没有积极使用触发expungeStaleEntries(). Map<String, WeakHashMap<...>>如果AntiCheat 使用类似WeakHashMap.

还有一个问题:在这种泄漏中,您可以看到WeakHashMap' 的队列将是非空的!然而它是空的,正如我检查的那样!我也注意到WeakHashMap被包裹synchronizedMap,但在我看来这可能不是问题。

这真的是由其中一个标志引起的 GC 错误吗?

这种模式每次都在每个堆转储中重复:

堆转储

标签: javagarbage-collectionjvm

解决方案


推荐阅读