Может ли сборщик мусора Java нарушить масштабирование HPA?

У меня есть Spring API с интенсивным использованием памяти, развернутый в кластере кубернетов.

Я настроил автоматическое масштабирование (HPA), чтобы рассматривать потребление памяти как критерий масштабирования, и запустив нагрузочный тест, все работает хорошо во время увеличения масштаба, однако во время уменьшения масштаба <сильный > память не разряжается и, следовательно, созданные поды не удаляются. Если я снова запущу тесты, будут созданы новые поды, но никогда не удаляемые.

Выполняя локальный анализ с использованием визуальной виртуальной машины, я считаю, что проблема связана с GC. Локально GC работает правильно во время теста, но в конце запросов он прекращает выполнение оставляя мусор позади, и он запускается снова только через долгое время. Поэтому я считаю, что этот оставленный мусор мешает масштабированию HPA.

Есть ли у кого-нибудь советы о том, что может вызывать этот эффект, или что-то, что я могу попробовать?

PS. В профилировщике у меня нет признаков утечки памяти, а когда я запускаю сборщик мусора вручную, оставшийся мусор удаляется.

Вот некоторые дополнительные сведения:

  • Версия Java: 11
  • Весенняя версия: 2.3
  • Версия Kubernetes: 1.17
  • Образ Docker: openjdk: 11-jre-slim
  • HPA запрашивает память: 1 Ги
  • Ограничения памяти HPA: 2 Ги
  • Показатели использования памяти HPA: 80%
  • Мин. Контейнеров HPA: 2
  • Количество капсул HPA Max: 8
  • Операционная система JVM: -Xms256m -Xmx1G

Тест Visual VM после нагрузки

Новая резидентная память Relic после нагрузочного теста


person Danilo Costa    schedule 05.04.2021    source источник
comment
прочтите это   -  person Eugene    schedule 06.04.2021


Ответы (1)


Скорее всего, утечки памяти нет.

JVM запрашивает память у операционной системы до предела, установленного параметром командной строки -Xmx.... После каждого основного запуска сборщика мусора JVM проверяет отношение используемой памяти кучи к (текущему) размеру кучи:

  • Если соотношение слишком близко к 1 (т.е. куча переполнена), JVM запрашивает память у ОС, чтобы увеличить размер кучи. Он делает это с энтузиазмом.

  • Если соотношение слишком близко к 0 (т. Е. Куча слишком велика), JVM может сжать кучу и вернуть часть памяти в ОС. Делает это неохотно. В частности, может потребоваться несколько запусков полного сборщика мусора, прежде чем JVM решит освободить память.

Я думаю, что вы видите последствия политики размера кучи JVM. Если JVM находится в режиме ожидания, полного GC не хватит, чтобы запустить JVM для сжатия кучи, и память не будет возвращена ОС.

Вы можете попытаться побудить JVM вернуть память, вызвав System.gc() несколько раз. Но запуск полного GC требует больших затрат ресурсов процессора. И если вам удастся заставить JVM сжать кучу, то повторное расширение кучи (для следующего большого запроса) повлечет за собой больше полных GC.

Так что мой совет: не пытайтесь это сделать. Используйте другие критерии для запуска автомасштабирования ... если это имеет смысл.


Также следует отметить, что приложение JVM + может использовать значительный объем памяти, отличной от кучи; например исполняемые и общие собственные библиотеки, собственная куча (C ++), стек потоков Java, метапространство Java и т. д. Ни одно из этих видов использования не ограничивается опцией -Xmx.

person Stephen C    schedule 06.04.2021