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

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

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

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

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

Ответы

Ответ 1

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

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

  • Установите JDK, если у вас его еще нет. Он поставляется с кучей опрятных инструментов.
  • Запустите приложение.
  • Откройте диспетчер задач и найдите идентификатор процесса (PID) для java.exe(или любой исполняемый файл, который вы используете). Если PID не отображается по умолчанию, используйте View > Select Columns..., чтобы добавить их.
  • Сбросьте кучу с помощью jmap.
  • Запустите сервер 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 - как известно, что класс [Ljava. lang.Object; действительно означает объект типа Object [].

Ответ 2

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

Подробнее о том, как работает Eclipse MAT, см. http://dev.eclipse.org/blogs/memoryanalyzer/2008/05/27/automated-heap-dump-analysis-finding-memory-leaks-with-one-click/.

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

Два важных урока, которые он изложил, следующие:

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

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

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

Ответ 8

Я использовал профилировщик Mykit Java (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

BR,
~ А

Ответ 9

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

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

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

Ответ 10

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

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

Ответ 11

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