Как я могу выяснить, что держится на несвободных объектах?

StackOverflow https://stackoverflow.com/questions/154309

Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

  1. Установите JDK, если у вас его еще нет. Он поставляется с кучей удобных инструментов.
  2. Запустите приложение.
  3. Откройте диспетчер задач и найдите идентификатор процесса (PID) для java.exe (или любого другого исполняемого файла, который вы используете).Если PID не отображаются по умолчанию, используйте «Вид» > «Выбрать столбцы»...чтобы добавить их.
  4. Сбросьте кучу, используя jmap.
  5. Запустите джхат сервер к созданному вами файлу и откройте браузер, чтобы http://локальный хост: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 использует - как знать, что класс [Ljava.lang.Object; на самом деле означает объект типа Объект[].

Другие советы

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

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

Я бы посмотрел на коллекции (особенно статические) в ваших классах (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 или какая-либо другая коллекция имеет ссылку на этот объект, сбор мусора не производится.

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

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

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

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

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

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

В нем изложены два важных урока:

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

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

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

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

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

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

БР,

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

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

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

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

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

Если вы получаете ошибки OOM на языке со сборщиком мусора, это обычно означает, что сборщик не учитывает некоторую часть памяти.Может быть, ваши объекты содержат ресурсы, отличные от Java?если да, то у них должен быть какой-то метод «закрытия», чтобы гарантировать, что ресурс будет освобожден, даже если объект Java не будет собран достаточно скоро.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top