Как я могу понять, что держится на незакрепленных объектах?
Одна из наших программ иногда получает ошибку 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 не собирается достаточно скоро.