Подтвердить что ты не робот

Утечка памяти в веб-приложении Java

У меня есть веб-приложение Java, работающее на Tomcat 7, которое, похоже, имеет утечку памяти. Среднее использование памяти приложения увеличивается линейно с течением времени при загрузке (определяется с помощью JConsole). После того, как использование памяти достигнет плато, производительность значительно ухудшается. Время отклика составляет от ~ 100 мс до [300 мс, 2500 мс], поэтому это фактически вызывает реальные проблемы.

Профиль памяти JConsole для моего приложения: application memory profile

Используя VisualVM, я вижу, что по меньшей мере половина памяти используется массивами символов (т.е. char []) и что большинство (примерно одинаковое количество каждого, 300 000 экземпляров) строк являются одним из следующих: "Ошибка распределения", "Копировать", "конец младшего GC", все из которых, похоже, связаны с уведомлением о сборе мусора. Насколько я знаю, приложение вообще не отслеживает сборщик мусора. VisualVM не может найти корень GC для любой из этих строк, поэтому мне трудно отслеживать это.

Сброс кучи памяти Analyzer: heap dump, unreachable memory

Я не могу объяснить, почему пласты использования памяти такие, но у меня есть теория о том, почему производительность ухудшается, как только это происходит. Если память фрагментирована, приложение может занимать много времени, чтобы выделить непрерывный блок памяти для обработки новых запросов.

Сравнивая это со встроенным приложением статуса сервера Tomcat, память увеличивается и выравнивается, но не попадает в высокий "пол", как мое приложение. Он также не имеет большого количества недостижимых char [].

Профиль памяти JConsole приложения статуса сервера Tomcat: enter image description here

Память анализатора памяти кучи состояния сервера Tomcat applicationp: enter image description here

Где могут быть выделены эти строки и почему они не собираются мусором? Существуют ли параметры Tomcat или Java, которые могут повлиять на это? Существуют ли определенные пакеты, которые могут повлиять на это?

4b9b3361

Ответ 1

Я удалил следующую конфигурацию JMX из tomcat\bin\setenv.bat:

set "JAVA_OPTS=%JAVA_OPTS% 
    -Dcom.sun.management.jmxremote=true 
    -Dcom.sun.management.jmxremote.port=9090
    -Dcom.sun.management.jmxremote.ssl=false
    -Dcom.sun.management.jmxremote.authenticate=false"

Я больше не могу получить подробные кучи памяти, но профиль памяти выглядит намного лучше: enter image description here

Спустя 24 часа профиль памяти выглядит одинаково: enter image description here

Ответ 2

Я бы предложил использовать memoryAnalyzer для анализа вашей кучи, он дает гораздо больше информации.
http://www.eclipse.org/mat/ существует автономное приложение и встроенное затмение. вам просто нужно запустить jmap на вашем приложении и проанализировать результат с этим.

Ответ 3

Я могу порекомендовать jvisualvm, который поставляется вместе с каждой установкой Java. Запустите программу, подключитесь к своему веб-приложению. Перейдите в Монитор → Куча дампа. Теперь это может занять некоторое время (в зависимости от размера). Навигация через кучу кучи довольно прост, но смысл, который вы должны выяснить сами (не слишком сложный, хотя), например.

Перейдите в Классы (внутри heapdump), выберите java.lang.String, щелкните правой кнопкой мыши Показать в представлении экземпляров. После этого вы увидите в таблице слева экземпляры String, которые в настоящий момент активны в вашей системе. Клик на одном экземпляре String, и вы увидите некоторые пресеты String в правой части правой верхней части правой строки, например, значение String.

В правой части внизу справа вы увидите, где этот экземпляр String ссылается на. Здесь вы должны проверить, на что ссылается большинство ваших * String * s. Но с вашим случаем (176/210, хорошей возможностью найти некоторые примеры строк, которые скоро вызовут ваши проблемы), после проверки, когда проблема остается, должно быть ясно.

Ответ 4

Плато вызвано тем, что доступная память падает ниже порогового значения по умолчанию, которое вызывает полный GC. Это объясняет, почему производительность падает, когда JVM постоянно приостанавливается, пытаясь найти и освободить память.

Обычно я советую смотреть на кэши объектов, но в вашем случае я думаю, что ваш размер кучи просто слишком мал для экземпляра Tomcat + webapp. Я бы рекомендовал увеличить вашу кучу до 1G (-Xms1024m -Xmx1024m), а затем снова рассмотреть использование памяти.

Если вы по-прежнему видите одно и то же поведение, тогда вы должны взять еще один кусок кучи и посмотреть на крупнейших потребителей после String и Char. По моему опыту, это обычно механизмы кэширования. Увеличьте свою память дальше или уменьшите хранилища кеширования, если это возможно. Некоторые кеши определяют только количество объектов, поэтому вам нужно понять, насколько велик каждый кешированный объект.

Как только вы поймете свое использование памяти, вы сможете опустить его снова, но IMHO 512MB будет минимальным.

Update:

Вам не нужно беспокоиться о недоступных объектах, поскольку они должны быть очищены GC. Кроме того, это нормально, что крупнейшими потребителями по типу являются String и Char - большинство объектов будут содержать какую-то строку, поэтому имеет смысл, что строки и символы являются наиболее распространенными по частоте. Понимание того, что содержит объекты, содержащие строки, является ключом к поиску потребителей памяти.

Ответ 5

Я столкнулся с одной и той же проблемой в совершенно другом приложении, поэтому tomcat7, вероятно, не виноват. Анализатор памяти показывает 10M недостижимых экземпляров String в этом процессе (который работает около 2 месяцев), и большинство/все они имеют значения, относящиеся к сборке мусора (например, "Отказ от распределения", "конец младшего GC" )

Анализатор памяти

Memory Analyzer

Полный GC теперь работает каждые 2 секунды, но эти строки не собираются. Я предполагаю, что мы попали в ошибку в коде GC. Мы используем следующую версию java:

$ java -version
java version "1.7.0_06"
Java(TM) SE Runtime Environment (build 1.7.0_06-b24)
Java HotSpot(TM) 64-Bit Server VM (build 23.2-b09, mixed mode)

и следующие параметры VM:

-Xms256m -Xmx768m -server -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC 
-XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:NewSize=32m -XX:MaxNewSize=64m
-XX:SurvivorRatio=8 -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails 
-Xloggc:/path/to/file

Ответ 6

Случайно я наткнулся на следующие строки в файле Tomcat conf/catalina.properties, который активирует кеширование строк. Это может быть связано с вашим делом, если у вас есть какие-либо из них. Кажется, что другие предупреждают, чтобы использовать эту функцию.

tomcat.util.buf.StringCache.byte.enabled=true
#tomcat.util.buf.StringCache.char.enabled=true
#tomcat.util.buf.StringCache.trainThreshold=500000
#tomcat.util.buf.StringCache.cacheSize=5000

Ответ 7

Поскольку это звучит неспецифично, одним кандидатом был бы JSF. Но тогда я ожидал, что и хеш-карты просачиваются.

Если вы используете JSF: В web.xml вы можете попробовать:

  • javax.faces.STATE_SAVING_METHOD клиент
  • com.sun.faces.numberOfViewsInSession 0
  • com.sun.faces.numberOfLogicalViews 1

Что касается инструментов: JavaMelody может быть интересен для постоянной статистики, но требует усилий.

Ответ 8

Попробуйте использовать MAT и убедитесь, что при анализе heapdump не выбрасывайте недостижимые объекты.

Чтобы сделать это, следуйте инструкциям здесь.

Затем вы можете запустить простой анализ Mem Leak (Это - хороший учебник)

Это должно быстро привести вас к первопричине.