Linux OOM-Killer и потребление памяти G1 GC

У меня есть приложение Java, работающее на Liberica JDK 8 (HotSpot VM, G1 GC) на машине Oracle Linux с 24 ГБ ОЗУ. Приложение имеет-Xmx15gмаксимальный размер кучи, интенсивно его использует (из-за профиля нагрузки) и является единственным процессом с такими требованиями к серверу.

Время от времени (обычно после десятков часов безотказной работы) приложение убивается Linux oom-killer . Чтобы найти основную причину, я включил JVM Native Memory Tracking (NMT) в подробном режиме, установил базовый уровень вскоре после прогрева и собрал следующую статистику за 34 часа безотказной работы (прямо перед тем, как процесс был снова остановлен):

Согласно документации по категориям памяти NMT Oracle , я ожидал, что самая тяжелая внутренняя категория будет заполнена чем-то вроде Direct ByteBuffers. Однако данные NMT показали, что почти 70% из этих 3 ГБ Internal состоят из таких распределений:

      [0x00007faeb7f9b5b5] BitMap::BitMap(unsigned long, bool)+0x1d5
[0x00007faeb82ca08f] OtherRegionsTable::add_reference(void*, int)+0x57f
[0x00007faeb82e0f40] InstanceKlass::oop_oop_iterate_nv(oopDesc*, FilterOutOfRegionClosure*)+0xc0
[0x00007faeb82c3373] HeapRegion::oops_on_card_seq_iterate_careful(MemRegion, FilterOutOfRegionClosure*, signed char*)+0x163
                             (malloc=1154790KB type=Internal +846638KB #577395 +423319)

Всего таких блоков было 8 сoops_on_card_seq_iterate_careful(…)вызовы методов, имеющие различные классы в следующем кадре стека, например:

  • InstanceRefKlass
  • InstanceKlass
  • ObjArrayKlass

Основываясь на этих идентификаторах, я выяснил, что эти подпрограммы являются частью G1 GC. Однако я не увидел способа повлиять на их поведение (потребление памяти) из параметров G1, доступных среди соответствующих опций JVM .

Глядя на этот связанный ответ SO , я попытался увеличить-XX:G1HeapRegionSizeс эргономически рассчитанных 4 МБ до установленных вручную 8 МБ, но существенных изменений не наблюдалось.

Итак, вопросы:

  1. Почему деятельность, связанная исключительно с G1, записывается NMT в категорию «Внутренние» ? (не GC)
  2. Есть ли способ заставить G1 потреблять меньше встроенной памяти? (ожидайте смены его на другой GC, что в данном случае невозможно)

1 ответ

Почему деятельность, связанная исключительно с G1, записывается NMT в категорию «Внутренние»? (не GC)

NMT не знает, что его действия связаны с G1: он не просматривает стек, чтобы узнать тип распределения, а просто использует тип, переданный функции распределения.

Как видно из трассировки стека, выделение происходит в конструкторе. Это класс общего назначения, используемый во многих местах, а не только в GC.BitMapкласс имеет распределитель, связанный сmtInternalтип:

        ArrayAllocator<bm_word_t, mtInternal> _map_allocator;

В более новых версиях JDK BitMap имеет тип распределения переменных, передаваемый извне.

Есть ли способ заставить G1 потреблять меньше встроенной памяти?

Перейдите на JDK 17 или новее. G1GC получил множество улучшений, которые никогда не будут перенесены в JDK 8. Настройка GC в JDK 8 — это золотая жила для консультантов по производительности, но если вы заботитесь о времени и ресурсах, обновление JDK — лучшая инвестиция.

Другие вопросы по тегам