Как WeakHashMap может утечь память в Java? - PullRequest
0 голосов
/ 21 октября 2019

Я использую большой сервер Minecraft (Spigot) с проприетарным, сильно запутанным плагином AntiCheat, а также с нашим собственным набором плагинов, проверенных в бою. Сервер много раз создает и выгружает новые миры, поэтому после выгрузки остаются CraftPlayer и CraftWorld объекты для GC.

Мы работаем на Java 13 (то же самое происходит на Java 12) с G1GC и так называемыми"aikar gc flags"

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 хранит слабую ссылку на CraftPlayer с, и все же они имеют утечку, и при достаточно большой утечке ЦП сервера очень сильно вращается, чтобы в конечном итоге умереть с OutOfMemoryError. Запущенный вручную System.gc() также не очищает их.

Когда AntiCheat отключен, утечка исчезла.

ЕДИНСТВЕННАЯ ссылка gc-root в heapdump на все утечки CraftWorlds и CraftPlayer - это запись в WeakHashMap, ключ CraftPlayer. (CraftPlayer и CraftWorld перекрестно ссылаются друг на друга, прежде чем стать обычным GCd).

Существует способ сделать утечку с помощью WeakHashMap: устаревшие «просроченные» записи не будут удалены, есливы не активно используете get () / set (), которая вызывает expungeStaleEntries(). Возможно, это было бы причиной, если бы AntiCheat использовал что-то вроде Map<String, WeakHashMap<...>>, но это не тот случай, он использует один основной WeakHashMap.

И здесь есть одна загвоздка: при такой утечке вы можете видеть, что очередь WeakHashMap будет не пустой! А пока пусто, как я проверял! Я также заметил, что WeakHashMap обернут synchronizedMap, но мне не кажется, что это может быть проблемой.

Это действительно ошибка GC, вызванная одним из флагов?

Этот шаблон повторяется каждый раз и в каждой куче:

heapdump

...