Метод для нахождения утечки памяти в больших дампах "кучи" Java

Я должен найти утечку памяти в JAVA-приложении. Я имею некоторый опыт с этим, но хотел бы совет относительно методологии/стратегии для этого. Любая ссылка и совет приветствуются.

О нашей ситуации:

  1. Дампы "кучи" больше, чем 1 ГБ
  2. У нас есть дампы "кучи" от 5 случаев.
  3. У нас нет тестового сценария для вызова этого. Это только происходит в (крупной) среде тестирования системы, по крайней мере, после недельного использования.
  4. Система основана на внутренне разработанной платформе прежней версии с таким количеством недостатков дизайна, что они невозможны считать их всех.
  5. Никто не понимает платформу подробно. Это было передано одному парню в Индии, который едва не отстает от ответа на электронные письма.
  6. Мы делали дампы "кучи" снимка со временем и приходили к заключению, что нет ни одного компонента, увеличивающегося со временем. Это - все, что медленно растет.
  7. Вышеупомянутое указывает на нас в направлении, что это - платформы система ORM собственной разработки, которая увеличивает ее использование без пределов. (Эта система карты возражает против файлов?! Так не действительно ORM)

Вопрос: Какова методология, которая помогла Вам успешно выполниться с выслеживанием утечек в приложении масштаба предприятия?

29
задан Rickard von Essen 24 March 2010 в 20:53
поделиться

6 ответов

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

Кроме того, вы не можете знать, является ли что-то утечкой или нет, не зная, зачем вообще существует этот класс.

Последние пару недель я занимался именно этим, причем использовал итерационный процесс.

Во-первых, я обнаружил, что профилировщики кучи практически бесполезны. Они не могут эффективно анализировать огромные кучи.

Вместо этого я полагался почти исключительно на jmap гистограммы.

Я предполагаю, что вы знакомы с ними, но для тех, кто не знаком:

jmap -histo:live <pid> > dump.out

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

Я выгружал кучу регулярно, каждые 5 минут, 24 часа в сутки. Это может быть слишком подробно для вас, но суть та же.

Я провел несколько различных анализов этих данных.

Я написал скрипт, который берет две гистограммы и выводит разницу между ними. Так, если java.lang.String была 10 в первом дампе, и 15 во втором, мой скрипт выдавал "5 java.lang.String", говоря мне, что она увеличилась на 5. Если бы она уменьшилась, число было бы отрицательным.

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

Однако некоторые классы сохраняются, а другие проходят GC'd. Эти классы могут легко увеличиваться и уменьшаться в целом, но при этом все равно утекать. Таким образом, они могут выпасть из категории "всегда растущих" классов.

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

Я нашел этот процесс очень успешным и действительно эффективным. Файлы гистограмм не безумно большие, и их было легко загрузить с хостов. Они не были очень дорогими для запуска на производственной системе (они заставляют выполнять большой GC, и могут немного заблокировать виртуальную машину). Я запускал их на системе с кучей Java в 2 Гб.

Теперь все, что это может сделать, это определить потенциально утекающие классы.

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

Например, вы можете обнаружить, что у вас много классов Map.Entry или других системных классов.

Если только вы не просто кэшируете String, факт в том, что эти системные классы, хотя и являются "нарушителями", не являются "проблемой". Если вы кэшируете какой-то класс приложения, то этот класс является лучшим индикатором того, где кроется ваша проблема. Если вы не кэшируете com.app.yourbean, то у вас не будет связанного с ним Map.Entry.

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

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

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

Профилировщик может помочь вам отследить владельцев этого "теперь утекающего" класса.

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

59
ответ дан 28 November 2019 в 00:49
поделиться

Я добился успеха с IBM Heap Analyzer . Он предлагает несколько представлений кучи, включая наибольшее падение размера объекта, наиболее часто встречающиеся объекты и объекты, отсортированные по размеру.

2
ответ дан 28 November 2019 в 00:49
поделиться

Можете ли вы ускорить время? Т.е. можете ли вы написать фиктивный тестовый клиент, который заставит его выполнить несколько недель вызовов/запросов и т.д. за несколько минут или часов? Это ваш самый большой друг, и если у вас его нет - напишите его.

Некоторое время назад мы использовали Netbeans для анализа дампов кучи. Он может быть немного медленным, но он был эффективным. Eclipse просто рухнул, и 32-разрядные инструменты Windows тоже.

Если у вас есть доступ к 64-разрядной системе или системе Linux с 3 ГБ или более, вам будет проще анализировать дампы кучи.

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

Когда все начало идти не так? Поговорите с людьми и попытайтесь узнать историю. Возможно, кто-то скажет: "Да, именно после того, как они исправили XYZ в патче 6.43, у нас начались странные вещи".

3
ответ дан 28 November 2019 в 00:49
поделиться

Я использовал jhat , это немного сурово, но это зависит от того, какой у вас фреймворк.

0
ответ дан 28 November 2019 в 00:49
поделиться

Взгляните на Eclipse Memory Analyzer . Это отличный инструмент (и самодостаточный, не требует установки самого Eclipse), который 1) может очень быстро открывать очень большие кучи и 2) имеет несколько довольно хороших инструментов автоматического обнаружения.Последний не идеален, но EMA предоставляет множество действительно хороших способов навигации и запроса объектов в дампе, чтобы найти возможные утечки.

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

13
ответ дан 28 November 2019 в 00:49
поделиться

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

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

1
ответ дан 28 November 2019 в 00:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: