Вопрос-ответ

How to find a Java Memory Leak

Как найти утечку памяти Java

Как найти утечку памяти в Java (используя, например, JHat)? Я попытался загрузить дамп кучи в JHat, чтобы получить общее представление. Однако я не понимаю, как я должен быть в состоянии найти корневую ссылку (ref) или как там это называется. В принципе, я могу сказать, что существует несколько сотен мегабайт записей хэш-таблицы ([java.util.Запись HashMap $ или что-то в этом роде), но карты используются повсеместно... Есть ли какой-нибудь способ искать большие карты или, возможно, находить общие корни деревьев больших объектов?

[Редактировать] Хорошо, я уже прочитал ответы, но давайте просто скажем, что я дешевый ублюдок (это означает, что я больше заинтересован в том, чтобы научиться использовать JHat, чем платить за JProfiler). Кроме того, JHat всегда доступен, поскольку он является частью JDK. Если, конечно, с JHat нет другого способа, кроме грубой силы, но я не могу поверить, что это может быть так.

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

Переведено автоматически
Ответ 1

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


  1. Запустите приложение и подождите, пока оно не перейдет в "стабильное" состояние, когда вся инициализация завершена и приложение находится в режиме ожидания.

  2. Запустите операцию, подозреваемую в утечке памяти, несколько раз, чтобы разрешить любую инициализацию, связанную с кэшем и БД.

  3. Запустите GC и сделайте снимок памяти.

  4. Запустите операцию еще раз. В зависимости от сложности операции и размеров обрабатываемых данных операцию может потребоваться выполнить несколько или много раз.

  5. Запустите GC и сделайте снимок памяти.

  6. Запустите diff для 2 моментальных снимков и проанализируйте его.

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

Для веб-приложений, которые обрабатывают запросы в нескольких потоках, анализ усложняется, но, тем не менее, общий подход по-прежнему применим.

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

Ответ 2

Вопрос: здесь я должен сказать, что получение инструмента, который не требует 5 минут для ответа на любой щелчок, намного упрощает поиск потенциальных утечек памяти.

Поскольку люди предлагают несколько инструментов (я пробовал только visual wm, поскольку получил его в пробной версии JDK и JProbe ) Хотя я должен предложить бесплатный инструмент с открытым исходным кодом, построенный на платформе Eclipse, анализатор памяти (иногда называемый анализатором памяти SAP), доступный на http://www.eclipse.org/mat / .

Что действительно здорово в этом инструменте, так это то, что он проиндексировал дамп кучи, когда я впервые открыл его, что позволило ему отображать данные, такие как сохраненная куча, без ожидания 5 минут для каждого объекта (практически все операции были намного быстрее, чем у других инструментов, которые я пробовал).

Когда вы открываете дамп, на первом экране отображается круговая диаграмма с самыми большими объектами (с учетом сохраненной кучи), и для удобства можно быстро перейти к объектам, которые являются слишком большими. В нем также есть функция поиска вероятных подозреваемых в утечке, которая, как я полагаю, может пригодиться, но поскольку навигации мне было достаточно, я толком не вникал в нее.

Ответ 3

Инструмент - это большое подспорье.

Однако бывают случаи, когда вы не можете использовать инструмент: дамп кучи настолько огромен, что инструмент выходит из строя, вы пытаетесь устранить неполадки на компьютере в какой-либо производственной среде, к которой у вас есть доступ только к командной строке, и т.д.

В таком случае полезно разобраться с файлом дампа hprof.

НАЧИНАЙТЕ поиск САЙТОВ. Здесь показано, какие объекты используют больше всего памяти. Но объекты объединены не только по типу: каждая запись также включает идентификатор "трассировки". Затем вы можете выполнить поиск по этой "трассировке nnnn", чтобы увидеть несколько верхних фреймов стека, где был выделен объект. Часто, как только я вижу, где выделен объект, я нахожу ошибку и все готово. Также обратите внимание, что вы можете контролировать, сколько кадров записывается в стек, с помощью опций -Xrunhprof .

Если вы проверяете сайт распределения и не видите ничего плохого, вам нужно запустить обратную цепочку от некоторых из этих живых объектов к корневым объектам, чтобы найти неожиданную цепочку ссылок. Здесь действительно помогает инструмент, но вы можете сделать то же самое вручную (ну, с помощью grep). Существует не только один корневой объект (т. Е. Объект, не подлежащий сборке мусора). Потоки, классы и фреймы стека действуют как корневые объекты, и все, на что они сильно ссылаются, не подлежит коллекционированию.

Чтобы выполнить объединение в цепочку, поищите в разделе ДАМПА КУЧИ записи с идентификатором неверной трассировки. Это приведет вас к записи OBJ или ARR, которая показывает уникальный идентификатор объекта в шестнадцатеричном формате. Выполните поиск по всем вхождениям этого идентификатора, чтобы определить, у кого есть сильная ссылка на объект. Следуйте каждому из этих путей в обратном направлении по мере их разветвления, пока не выясните, где находится утечка. Видите, почему инструмент так удобен?

Статические элементы часто приводят к утечкам памяти. На самом деле, даже без инструмента стоило бы потратить несколько минут на поиск в вашем коде статических элементов карты. Может ли карта увеличиваться в размерах? Что-нибудь когда-нибудь очищает свои записи?

Ответ 4

В большинстве случаев в корпоративных приложениях указанная куча Java превышает идеальный размер от 12 до 16 ГБ. Мне было сложно заставить профилировщик NetBeans работать непосредственно с этими большими Java-приложениями.

Но обычно это не требуется. Вы можете использовать утилиту jmap, которая поставляется вместе с jdk, для создания "живого" дампа кучи, то есть jmap создаст дамп кучи после запуска GC. Выполните некоторую операцию над приложением, дождитесь завершения операции, затем сделайте еще один "живой" дамп кучи. Используйте инструменты, такие как Eclipse MAT, для загрузки дампов, сортировки на гистограмме, просмотра того, какие объекты увеличились или какие являются самыми высокими, это даст подсказку.

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

С этим подходом связана только одна проблема; Огромные дампы кучи, даже при использовании опции live, могут быть слишком большими для передачи на этап разработки, и для их открытия может потребоваться компьютер с достаточным объемом памяти / RAM.

Вот тут-то и проявляется гистограмма класса. Вы можете создать динамическую гистограмму класса с помощью инструмента jmap. Это даст только гистограмму использования памяти классом.В принципе, у него не будет информации для объединения ссылки в цепочку. Например, он может поместить массив char вверху. А класс String где-то внизу. Вы должны установить соединение самостоятельно.

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

Вместо получения двух дампов кучи, возьмите две гистограммы классов, как описано выше; Затем сравните гистограммы классов и посмотрите, какие классы увеличиваются. Посмотрите, можете ли вы связать классы Java с классами вашего приложения. Это даст довольно хорошую подсказку. Вот скрипт pythons, который может помочь вам сравнить два дампа гистограммы jmap. histogramparser.py

Наконец, такие инструменты, как JConolse и VisualVM, необходимы для отслеживания роста объема памяти с течением времени и определения наличия утечки памяти. Наконец, иногда ваша проблема может заключаться не в утечке памяти, а в высоком использовании памяти.Для этого включите ведение журнала GC; используйте более продвинутый и новый сжимающий GC, такой как G1GC; и вы можете использовать инструменты jdk, такие как jstat, чтобы увидеть поведение GC в реальном времени

jstat -gccause pid <optional time interval>

Другие ссылки на Google для поиска -jhat, jmap, Full GC, Humongous allocation, G1GC

2023-06-26 07:58 java