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

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

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

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

13 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Вот где гистограмма класса входит в картину. Вы можете вывести гистограмму живого класса с помощью инструмента jmap. Это даст только гистограмму класса использования памяти. В основном у нее не будет информации, чтобы связать ссылку. Например, он может поместить массив символов сверху. И класс 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 распределения, G1GC

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

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

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

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

Вы можете узнать, измерив объем используемой памяти после многократного вызова сборщика мусора:

Runtime runtime = Runtime.getRuntime();

while(true) {
    ...
    if(System.currentTimeMillis() % 4000 == 0){
        System.gc();
        float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
        System.out.println("Used memory: " + usage + "Mb");
    }

}

Если выходные числа были равны, в вашем приложении нет утечки памяти, но если вы заметили разницу между числами использования памяти (увеличивающиеся числа), в вашем проекте есть утечка памяти. Например:

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

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

Заказ этого скринкасто о поиске утечек памяти с JProfiler. Это визуальное объяснение ответа @Dima Malenko.

Примечание: хотя JProfiler не является бесплатным, но пробная версия может справиться с текущей ситуацией.

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

Поскольку большинство из нас уже используют Eclipse для написания кода, почему бы не использовать инструмент Memory Analyzer Tool(MAT) в Eclipse. Отлично работает.

Затмение MAT представляет собой набор плагинов для Eclipse IDE, которая предоставляет инструменты для анализаheap dumps из приложения Java и для идентификации memory problems в приложении.

Это помогает разработчику находить утечки памяти с помощью следующих функций

  1. Получение снимка памяти (дамп кучи)
  2. Гистограмма
  3. Сохраненная куча
  4. Доминатор Дерево
  5. Изучение путей к корням сборщика мусора
  6. Инспектор
  7. Общие антипаттерны памяти
  8. Язык объектных запросов

Недавно я столкнулся с утечкой памяти в нашем приложении. Делюсь своим опытом здесь

Сборщик мусора периодически удаляет объекты, на которые нет ссылок, но никогда не собирает объекты, на которые все еще ссылаются. Вот где могут возникнуть утечки памяти.

Вот несколько вариантов, как найти объекты, на которые есть ссылки.

  1. С помощью jvisualvm который находится в папке

Параметры: смотреть пространство кучи

Если вы видите, что пространство кучи продолжает увеличиваться, определенно есть утечка памяти.

Чтобы выяснить причину, вы можете использовать memory sampler под sampler .

  1. Получите гистограмму кучи Java с помощью jmap (который также доступен в JDK/bin папка) в разном временном промежутке приложения

            jmap -histo <pid> > histo1.txt
    

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

Вы можете прочитать некоторые из наиболее распространенных причин утечки памяти здесь, в этой статье: Общие сведения об утечках памяти в Java

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

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