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

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

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

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

9 ответов


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

  1. запустите приложение и дождитесь, пока оно перейдет в "стабильное" состояние, когда вся инициализация будет завершена и приложение будет бездействовать.
  2. запустите операцию, подозреваемую в создании утечки памяти несколько раз, чтобы разрешить любой кэш, Инициализация, связанная с БД.
  3. запустите GC и сделайте снимок памяти.
  4. повторите операцию. В зависимости от сложности операции и размеров обрабатываемых данных операций может потребоваться выполнить несколько раз.
  5. запустите GC и сделайте снимок памяти.
  6. запустите diff для 2 снимков и проанализируйте его.

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

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

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


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

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

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

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


инструмент-большая помощь.

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

в этом случае это помогает узнать ваш путь вокруг файла дампа hprof.

поиск сайтов начинается. Это показывает, какие объекты используют больше всего памяти. Но объекты не сгруппированы вместе только по типу: каждая запись также включает идентификатор" трассировки". Затем вы можете найти эту "трассировку nnnn", чтобы увидеть несколько верхних кадров стека, в котором был выделен объект. Часто, как только я вижу, где выделен объект, я нахожу ошибку, и все готово. Кроме того, обратите внимание, что вы можете управлять количеством кадров, записанных в стеке с параметрами-Xrunhprof.

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

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

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


большую часть времени в корпоративных приложениях куча 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 с помощью утилиты 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 live

jstat -gccause pid <optional time interval>

другое referecences в гугле -она, jmap, полный GC, гомогенные выделения, G1GC


есть инструменты, которые должны помочь вам найти утечку, такие как JProbe, YourKit, AD4J или JRockit Mission Control. Последнее я знаю лучше всех. Любой хороший инструмент должен позволить вам детализировать до уровня, где вы можете легко определить, какие утечки и где выделяются протекающие объекты.

использование HashTables, Hashmaps или подобных является одним из немногих способов, которыми вы можете acually утечка памяти в Java вообще. Если бы мне пришлось найти утечку от руки, я бы peridically печатать размер моих хэш-карт, и оттуда найти тот, где я добавляю элементы и забываю их удалить.


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


вам действительно нужно использовать профилировщик памяти, который отслеживает распределения. Взгляните на JProfiler - их функция "heap walker" отличная, и у них есть интеграция со всеми основными ИД Java. Это не бесплатно, но это не так дорого ($499 за одну лицензию) - вы будете сжигать время на 500 долларов довольно быстро, пытаясь найти утечку с менее сложными инструментами.


NetBeans имеет встроенный профилировщик.


вы можете ознакомиться утилиты jconsole. Это также часть JDK, и я нашел полезным найти утечки памяти/ссылки в сочетании с jhat. Также взгляните на этой запись в блоге.