Это почти невозможно без некоторого понимания базового кода. Если вы понимаете лежащий в основе код, тогда вы можете лучше отсортировать пшеницу от мусора из миллиарда битов информации, которые вы получаете в своих кучах.
Кроме того, вы не можете знать, является ли что-то утечка или нет, не зная, почему класс существует вообще.
Я просто провел последние пару недель, занимаясь именно этим, и использовал итеративный процесс.
Во-первых, я обнаружил, что профилировщики кучи бесполезны. Они не могут эффективно анализировать огромные кучи.
Скорее, я полагался почти исключительно на 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 2G.
Теперь все, что можно сделать, - это определить потенциально протекающие классы.
Именно здесь приходит понимание того, как используются классы, и должны ли они быть или не должны быть их.
Например, вы можете обнаружить, что у вас много классов Map.Entry или другого системного класса.
Если вы просто не кешируете String, дело в том, что эти системные классы, хотя, возможно, «нарушители», не являются «проблемой». Если вы кэшируете какой-то класс приложения, то этот класс является лучшим индикатором того, в чем заключается ваша проблема. Если вы не кешируете com.app.yourbean, к нему не будет привязан связанный Map.Entry.
Как только у вас появятся классы, вы можете начать сканирование базы кода в поисках экземпляров и ссылок. Поскольку у вас есть собственный слой ORM (хорошо это или плохо), вы можете по крайней мере с готовностью просмотреть его исходный код. Если ваш ORM выполняет кэширование, вероятно, он кэширует классы ORM, оборачивая классы вашего приложения.
Наконец, еще одна вещь, которую вы можете сделать, когда вы знаете классы, вы можете запустить локальный экземпляр сервера с гораздо меньшей кучей и меньшим набором данных, а также использовать один из профилировщиков.
В этом случае вы можете выполнить модульное тестирование, которое затрагивает только 1 (или небольшое число) из тех вещей, которые, по вашему мнению, могут протекать. Например, вы можете запустить сервер, запустить гистограмму, выполнить одно действие и снова запустить гистограмму. Ваш текущий класс должен был увеличиться на 1 (или какова бы ни была ваша единица работы).
Профилировщик может помочь вам отследить владельцев этого "теперь просочившегося" класса.
Но, в конце концов, вам понадобится некоторое понимание вашей кодовой базы, чтобы лучше понять, что такое утечка, а что нет, и почему объект вообще существует в куче, тем более, почему он может быть сохраненным как утечка в вашей куче.