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

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

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

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

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

Если вы ищете бесплатный профилировщик, я предлагаю вам взглянуть на jiprof.sourceforge.net. Может быть, немного старомодно, без модного графического интерфейса и т. д., Но работает в большинстве случаев.

Daniel Hiller 01.10.2008 14:53
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
15
1
8 894
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

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

Обновлено: удалено неправильное замечание на WeakReference.

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

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

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

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

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

Если бы после первого System.gc () не было дальнейших ссылок на «карту», ​​разве это не было бы сборщиком мусора?

Paul Tomblin 30.09.2008 23:12

Да, если на HashMap больше ничего не ссылается, тогда он будет иметь право на сборку мусора.

18Rabbit 30.09.2008 23:17

Возможно, это изменилось, но разве JVM не игнорирует вызов system.gc и не делает ли это всякий раз, когда ей хочется?

tloach 30.09.2008 23:19

Согласно API, «вызов метода gc предполагает, что виртуальная машина Java затрачивает усилия на переработку неиспользуемых объектов, чтобы сделать память, которую они в настоящее время занимают, доступной для быстрого повторного использования». Итак, вы как бы вежливо спрашиваете и намекаете, что хотите, чтобы это было сделано.

18Rabbit 30.09.2008 23:22

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

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

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

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

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

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

Paul Tomblin 30.09.2008 23:32

В п. 24 Блоха «Эффективная Java». Правило 6: Удалите устаревшие ссылки на объекты, есть пример плохо реализованного Stack, где его элементы кэшируются в элементах Object [], но они никогда не обнуляются из массива elements при вызове pop ().

Eugene Yokota 01.10.2008 10:14

Попробуйте 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.

Ответ принят как подходящий

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

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

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

Будьте осторожны при создании дампа с помощью jmap: вам может потребоваться запустить jmap от имени пользователя jvm, к которому вы подключаетесь. См. это.

phs 09.01.2014 01:06

также будьте осторожны, вам нужно предоставить дополнительную память кучи для jhat, например "-J-mx6G", для успешного запуска сервера для дампа кучи 4 ГБ

kommradHomer 23.12.2020 17:26

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

BR,
~ А

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

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

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

Нет, меня уволили. Так что это больше не моя проблема.

Paul Tomblin 09.02.2009 15:23

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

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

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