Профилирование памяти для настольного Java-приложения - PullRequest
3 голосов
/ 18 ноября 2009

Мое приложение загружает набор данных ок. От 85 до 100 МБ каждый раз. Ограничение памяти приложения составляет 512 МБ, и этого, теоретически, более чем достаточно.

Однако я обнаружил, что если при одном запуске приложения я открывал и закрывал набор данных 5 раз, общее потребление памяти неуклонно возрастало, пока я не получил ошибку нехватки памяти:

 PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+   COMMAND
6882 bguiz 20 0 679m 206m 19m S 30   13.7 0:30.22 java
6882 bguiz 20 0 679m 259m 19m S 9    17.2 0:55.53 java
6882 bguiz 20 0 679m 301m 19m S 9    20.0 1:20.04 java
6882 bguiz 20 0 679m 357m 19m S 33   23.7 1:44.74 java
6882 bguiz 20 0 679m 395m 19m S 80   26.2 2:10.31 java

Память выросла с ~ 14% до ~ 26%. Похоже, утечка памяти.

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

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


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

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

Я прочитал некоторые другие вопросы SO, в которых задавались вопросы о том, какие инструменты профилирования памяти использовать, и я решил использовать тот, который встроен в IDE Netbeans, поскольку он, похоже, имел хорошие отзывы, и я все равно работаю в Netbeans.

Кто-нибудь занимался подобной задачей профилирования памяти Java раньше и задним числом:

  • Какой конкретный совет вы бы мне дали?
  • Какие методы вы нашли полезными для решения этой проблемы?
  • Какие ресурсы вы нашли полезными для решения этой проблемы?

Edit: Это стандартное настольное приложение, а не веб-приложение.


Редактировать: Внедренное решение

В основном, для меня работало использование профилировщика Netbeans в сочетании с JHAT.

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

Однако он не дал мне возможности сравнить два дампа кучи. Я задал следующий вопрос , и похоже, что JHAT (входит в JDK) справляется с этой работой довольно хорошо.

Торбьерн Равн Андерсен, Дмитрий и Джейсон Гритман: ваш вклад был действительно полезным, к сожалению, я могу отметить только 1 как правильный ответ, и вы все равно получили +1 от меня.

Ответы [ 7 ]

3 голосов
/ 18 ноября 2009

Я написал ответ на другой вопрос о методах поиска утечек памяти на https://stackoverflow.com/questions/1716597/java-memory-leak-detection-tools/1717260#1717260

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

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

2 голосов
/ 18 ноября 2009

Присоединитесь к вашей программе с помощью jvisualvm из Java 6 JDK и посмотрите, куда уходит ваша память.

1 голос
/ 18 ноября 2009

Профилировщик NetBeans, вероятно, лучший из бесплатных. Он хорошо справляется со своей работой, особых подсказок по работе с самим профилировщиком нет. Что касается вашего кода, обратите внимание на хеш-карты, где вы что-то кешируете (особенно статические). Очень часто они являются источниками утечек памяти. Попробуйте использовать WeakHashMap для кэширования (грубо говоря, он не создает сильных ссылок на кэшированные значения).

0 голосов
/ 18 ноября 2009

Eclipse Memory Analyzer также является хорошим автономным инструментом для анализа дампов кучи.
У вас есть несколько вариантов создания дампа кучи (например, в OutOfMemoryExceptions).

0 голосов
/ 18 ноября 2009

Ищите static Коллекции (Карта, Набор, Список), используемые в качестве кэшей.

0 голосов
/ 18 ноября 2009

Вы изменяете распределение памяти при запуске приложения? Например:

java -Xmx512m -Xms128m foo.Bar

Насколько я понимаю, ошибка нехватки памяти также может возникнуть, если JVM не может выделить память достаточно быстро. Несмотря на то, что он имеет потолок 512 м (в приведенном выше примере), если JVM не может выделить память достаточно быстро (за пределы начальных 128 М, указанных выше), может возникнуть ошибка нехватки памяти. Если вы начнете с более высокого значения -Xms, это может решить проблему. Также важно отметить, что значения Xms и Xmx являются предложениями , а не жесткими правилами.

0 голосов
/ 18 ноября 2009

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

В качестве первой попытки я, возможно, попытался бы запустить программу с новым G1 сборщиком мусора , который доступен с Java 1.6.0_14. При нормальных обстоятельствах, вероятно, лучше достигнуть заявленных экземпляров раньше, а также имеет преимущество, заключающееся в возможности возврата неиспользуемой памяти обратно в операционную систему. Другие сборщики мусора Java имеют проблему, заключающуюся в том, что память, выделенная из ОС, обычно не возвращается до завершения процесса.

...