Предпосылки:
- ПК с 16 ГБ оперативной памяти
- JDK 1.8.x установлен на Ubuntu 16.10 x64.
- стандартное веб-приложение на основе Spring, которое развертывается на Tomcat 8.5.x. Tomcat настроен со следующими параметрами:
CATALINA_OPTS="$CATALINA_OPTS -Xms128m -Xmx512m -XX:NewSize=64m -XX:MaxNewSize=128m -Xss512k -XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods -XX:MaxMetaspaceSize=512m -XX:-TieredCompilation -XX:ReservedCodeCacheSize=512m"
- JMeter 2.13 для запуска нагрузочных тестов
- JProfiler 9.x для отслеживания использования памяти кучи java
-
top
использовать для отслеживания использования памяти процесса Java
Когда я запускаю тесты нагрузки последовательно 3 раза, я наблюдаю (используя top
), что java-процесс увеличивает количество используемой памяти:
- После запуска Tomcat он использует ~ 1Gb
- после первого запуска теста он использует 4.5Gb
- Когда все тесты завершены, Tomcat использует 7 ГБ оперативной памяти.
Все это время размер кучи ограничен, и JProfiler подтверждает, что размер кучи не превышает 512 МБ.
Это скриншот JProfiler. Красные цифры внизу - это размер памяти, используемый java-процессом (согласно top
).
Вопрос: почему процесс Java продолжает увеличивать использование памяти во время работы?
Спасибо!
UPD # 1: О возможном дублировании: они have confirmed that this only happens on Solaris.
, но я использую Ubuntu 16.10. Кроме того, у остроконечного вопроса нет ответа, который бы объяснил причину проблемы.
UPD # 2: мне пришлось вернуться к этому вопросу после некоторой паузы. И теперь я использую pmap
util для создания дампа памяти, используемого процессом java
. У меня есть три дампа: до запуска тестов, после выполнения первых тестов и после некоторых N тестов. Тесты приносят много трафика в приложение. Все дампы находятся здесь: https://gist.github.com/proshin-roman/752cea2dc25cde64b30514ed9ed9bbd0. Они довольно огромные, но самые интересные вещи находятся на 8-й линии с размером кучи: перед тестированием он занимает 282.272 Kb
и 3.036.400 Kb
- более 10 раз! И он растет каждый раз, когда я запускаю тесты. В то же время размер кучи является постоянным (согласно JProfiler/VisualVM). Какие варианты я должен найти для причины этой проблемы? Debug JVM? Я попытался найти способы "взглянуть" на этот сегмент памяти, но не смог. Итак:
- Можно ли каким-либо образом определить сегмент памяти
[heap]
? - Ожидается ли такое поведение java?
Я буду благодарен за любые советы по этой проблеме. Спасибо всем!
UPD # 3: используя jemalloc (спасибо @ivan за идею), я получил следующее изображение:
И похоже, что у меня почти такая же проблема, как описано здесь: http://www.evanjones.ca/java-native-leak-bug.html
UPD # 4: на данный момент я обнаружил, что проблема связана с java.util.zip.Inflater/Deflater, и эти классы используются во многих местах моего приложения. Но наибольшее влияние на потребление памяти делает взаимодействие с удаленным SOAP-сервисом. В моем приложении используется эталонная реализация стандарта JAX-WS, и он дал следующее потребление памяти при загрузке (он имеет низкую точность после 10Gb): Затем я выполнил те же тесты нагрузки, но с реализацией Apache CXF и дал следующий результат: Таким образом, вы можете видеть, что CXF использует меньше памяти, и он более стабилен (он не растет все время как ref.impl.). Наконец, я обнаружил проблему с отслеживателем проблем JDK - https://bugs.openjdk.java.net/browse/JDK-8074108 - снова об утечках памяти в zip-библиотеке, и проблема еще не закрыта. Таким образом, похоже, что я не могу устранить проблему с утечками памяти в своем приложении, просто могу сделать некоторые обходные пути.
Спасибо всем за вашу помощь!