Как я могу выяснить, что держит несвободные объекты? - PullRequest
14 голосов
/ 30 сентября 2008

Одна из наших программ иногда получает ошибку OutOfMemory на компьютере одного пользователя, но, конечно, не во время ее тестирования. Я только что запустил его с JProfiler (на 10-дневной пробной лицензии, потому что я никогда не использовал его) и отфильтровал по нашему префиксу кода, самый большой кусок как по общему размеру, так и по количеству экземпляров составляет более 8000 экземпляров определенного простого класса ,

Я нажал кнопку «Сбор мусора» на JProfiler, и большинство других наших классов исчезли, но не эти особые. Я снова запустил тест, все еще в том же экземпляре, и он создал более 4000 экземпляров класса, но когда я нажал «Сбор мусора», они ушли, оставив более 8000 экземпляров.

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

Какие-нибудь предложения, как я могу выяснить, что держит ссылку? Я ищу предложения о том, что искать в коде, а также способы выяснить это в JProfiler, если есть.

Ответы [ 11 ]

19 голосов
/ 30 сентября 2008

Сбросьте кучу и осмотрите ее.

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

  1. Установите JDK, если у вас его еще нет. Поставляется с кучей аккуратных инструментов.
  2. Запустите приложение.
  3. Откройте диспетчер задач и найдите идентификатор процесса (PID) для java.exe (или любого исполняемого файла, который вы используете). Если PID по умолчанию не отображаются, используйте Вид> Выбрать столбцы ..., чтобы добавить их.
  4. Дамп кучи, используя jmap .
  5. Запустите сервер jhat для файла, который вы создали, и откройте в браузере значение http://localhost:7000 (порт по умолчанию - 7000). Теперь вы можете просматривать интересующий вас тип и информацию, такую ​​как количество экземпляров, ссылки на них и т. Д.

Вот пример:

C:\dump>jmap -dump:format=b,file=heap.bin 3552

C:\dump>jhat heap.bin
Reading from heap.bin...
Dump file created Tue Sep 30 19:46:23 BST 2008
Snapshot read, resolving...
Resolving 35484 objects...
Chasing references, expect 7 dots.......
Eliminating duplicate references.......
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

Чтобы интерпретировать это, полезно понять некоторые из номенклатуры типов массивов Java использует - например, зная, что class [Ljava.lang.Object; действительно означает объект типа Объект [] * * тысяча тридцать-четырь.

9 голосов
/ 30 сентября 2008

Попробуйте Eclipse Memory Analyzer. Для каждого объекта будет показано, как он подключен к корню GC - объект, который не является сборщиком мусора, поскольку он поддерживается JVM.

См. http://dev.eclipse.org/blogs/memoryanalyzer/2008/05/27/automated-heap-dump-analysis-finding-memory-leaks-with-one-click/ для получения дополнительной информации о том, как работает Eclipse MAT.

5 голосов
/ 30 сентября 2008

Я бы посмотрел на Коллекции (особенно статические) в ваших классах (HashMaps - хорошее место для начала). Возьмите этот код, например:

Map<String, Object> map = new HashMap<String, Object>(); // 1 Object
String name = "test";             // 2 Objects
Object o = new Object();          // 3 Objects
map.put(name, o);                 // 3 Objects, 2 of which have 2 references to them

o = null;                         // The objects are still being
name = null;                      // referenced by the HashMap and won't be GC'd

System.gc();                      // Nothing is deleted.

Object test = map.get("test");    // Returns o
test = null;

map.remove("test");               // Now we're down to just the HashMap in memory
                                  // o, name and test can all be GC'd

Пока HashMap или какая-либо другая коллекция имеет ссылку на этот объект, она не будет собирать мусор.

3 голосов
/ 30 сентября 2008

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

2 голосов
/ 30 сентября 2008

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

Также помните, что вы можете получить OOME, потому что gc не удалось собрать достаточно памяти, несмотря на то, что на самом деле было достаточно для создания запроса объекта. В противном случае спектакль рухнул бы в землю.

2 голосов
/ 30 сентября 2008

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

Редактировать: удалено некорректное замечание по WeakReference.

1 голос
/ 31 марта 2009

Некоторые предложения:

  • Неограниченное количество карт, используемых в качестве кешей, особенно при статических
  • ThreadLocals в серверных приложениях, потому что потоки обычно не умирают, поэтому ThreadLocal не освобождается
  • Внутренние строки (Strings.intern ()), что приводит к куче строк в PermSpace
1 голос
/ 09 февраля 2009

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

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

Вы решили проблему?

1 голос
/ 30 сентября 2008

Я использовал профилировщик Java Yourkit (http://www.yourkit.com) для оптимизации производительности на Java 1.5. В нем есть раздел о том, как работать с утечками памяти. Я считаю его полезным.

http://www.yourkit.com/docs/75/help/performance_problems/memory_leaks/index.jsp

Вы можете получить 15-дневный eval: http://www.yourkit.com/download/yjp-7.5.7.exe

BR
~ A

1 голос
/ 30 сентября 2008

Я только что прочитал статью об этом, но мне жаль, я не могу вспомнить, где. Я думаю, что это могло быть в книге «Эффективная Java». Если я найду ссылку, я обновлю свой ответ.

Вот два важных урока:

1) Финальные методы говорят gc, что делать, когда он отбирает объект, но он не просит его сделать это, и нет способа требовать, чтобы он это делал.

2) Современный эквивалент «утечки памяти» в неуправляемых средах памяти - это забытые ссылки. Если вы не установите для всех ссылок на объект значение null , когда закончите, объект никогда не будет отбракован. Это наиболее важно при реализации собственного вида Collection или вашей собственной оболочки, которая управляет Collection. Если у вас есть пул, или стек, или очередь, и вы не устанавливаете корзину на null , когда вы «удаляете» объект из коллекции, корзина, в которой находился объект, будет поддерживать этот объект живым пока этот контейнер не будет установлен для ссылки на другой объект.

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

...