Проблемы со сборщиком памяти/мусора в службе Java Spring

У меня проблема с памятью в службе Java (21), использующей среду Spring (3.2.1).

Сервис довольно простой. Он предоставляет конечную точку REST.

Когда вызывается эта конечная точка, служба отправляет HTTP-запрос другой службе (используя механизм RESTCLIENT Spring) для получения данных. Как только данные получены, они возвращаются.

Данных много, поэтому, когда я звоню на свою конечную точку, происходит всплеск использования памяти, что меня не удивляет.

Однако после завершения обработки объем памяти остается высоким и никогда не отключается.

Вот скриншот из VisualVM:

Память остается высокой, пока я не нажму «Выполнить сбор мусора» в VisualVM. Я пробовал, даже через 24 часа память все еще остается высокой, даже если никто не звонит на мою конечную точку.

Вот дополнительная информация:

  • Служба работает в контейнере Docker. Используемое изображение: 3.9.5-eclipse-temurin-21.
  • Я попытался настроить параметры JVM:
    • Без каких-либо параметров
    • -Xms128m -XX:+UseG1GC -Xmx3g
    • -Xms128m -XX:+UseG1GC -Xmx3g -XX:MaxGCPauseMillis=200 Во всех трёх случаях проблема остаётся.

Есть ли у вас какие-либо идеи о происхождении проблемы и возможных решениях?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это нормальное поведение. Сборщики мусора Java предназначены для минимизации двух показателей:

  • Загрузка процессора сборщиком мусора и/или
  • «Остановить мир» приостанавливается, пока работает сборщик мусора.

Минимизация общего использования памяти не является основной целью.

В вашем примере вы используете G1GC с (явной или неявной) целью максимального времени паузы. JVM видит, что еще есть ~1 ГБ свободного места в куче, и оценивает, что ей еще не нужно запускать сборщик мусора, чтобы обеспечить соблюдение гарантий времени паузы. Так что это не так... потому что запуск сборщика мусора, когда мусора недостаточно, неэффективен.

Честно говоря, я не думаю, что это вообще проблема. Но если среднее использование памяти действительно является для вас проблемой, обратите внимание на варианты G1PeriodicGC. Они сообщают JVM периодически запускать сборщик мусора при условии, что JVM еще не (слишком) занята. Подробности см. JEP 346. Эта функция была реализована в Java 12, поэтому она должна быть доступна в образе Java 21, который вы (я думаю) используете.

Примечание:

  • Это увеличит загрузку процессора вашей JVM: TANSTAAFL.
  • Если у вас есть основная проблема с пиковым использованием кучи, это не решит ее. Когда объем доступных данных достигнет пика, JVM потребуется достаточно оперативной памяти для их хранения... плюс еще немного из-за политики размера кучи JVM. Единственный способ избежать использования JVM слишком большого количества оперативной памяти в пиковые периоды использования — установить максимальный размер кучи, который не позволит ей это делать.

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