Почему JVM так долго собирает мой недоступный объект? - PullRequest
9 голосов
/ 13 августа 2011

Я работал над утечкой загрузчика классов в нашем приложении и, наконец, дошел до того, что все ссылки на CL исчезли.В моем инструменте профилирования памяти (используя YourKit и jmap / jhat) я могу принудительно заставить GC, который иногда немедленно избавляется от моего загрузчика классов, но иногда в других случаях (зависит от использования приложения, но это настолько специфично, насколько я могу получить), когда я принудительноGC, экземпляр CL не исчезает.Я делаю снимок памяти и смотрю на результаты, и он говорит, что этот объект существует, но недоступен.

Вот дурацкая часть ... и я обнаружил это только случайно.

Iможет заставить полный GC все, что я хочу, но экземпляр просто не уходит.ОДНАКО через 10-20 минут, если я сделаю еще один полный сборщик мусора, он действительно будет получен.

(В течение этого периода времени приложение в основном неактивно (не полностью). Мое "продлено"«Перерыв на кофе был несчастным случаем, который привел к этому открытию.)

Так что мое беспокойство по поводу утечки здесь исчезло (надеюсь), но теперь вопрос больше состоит в попытках объяснить это поведение.

Кто-нибудь знает, что может заставить Sun JVM принять решение не собирать недоступный загрузчик классов в течение 20 минут?

Подробности версии Sun JVM:

java version "1.6.0_23"
Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode)

Ответы [ 4 ]

5 голосов
/ 13 августа 2011

Я думаю, что Джереми Хейлер находится на правильном пути, поскольку это проблема финализации.По сути, даже если GC знает, что объект недостижим, до того, как объект действительно будет собран, может пройти неопределенное количество времени, поскольку он может быть поставлен в очередь для завершения.Поток финализатора должен дойти до завершения объекта (что может занять некоторое время, в зависимости от того, сколько других объектов нужно завершить и как выглядят их финализаторы), а затем, после того, как объект был завершен, вам понадобится еще один GC, чтобы заново обнаружить, что объект недоступен до того, как он действительно будет собран.

Плюс, в случае ClassLoader на Sun JVM, он, вероятно, размещен непосредственно в PermGen.Таким образом, вам понадобится не один, а два полных цикла PermGen (один, чтобы получить объект в очередь на завершение, а затем второй, чтобы фактически собрать его).Поскольку развертки PermGen стоят дорого, я считаю, что Sun JVM вообще не выполняет их с настройками по умолчанию, и даже с различными настройками GC, все еще довольно неохотно сканировать PermGen (особенно, если не много другого давления памяти).

Если вместо форсирования (эм, я имею в виду, поощрение ) GC, используя System.gc(), что если вы сделаете что-то вроде:

System.gc();
System.runFinalization();
System.gc();

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

0 голосов
/ 09 декабря 2013

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

-XX:SoftRefLRUPolicyMSPerMB=1

и посмотрите, имеет ли это значение.Вот что произошло в моем случае .

0 голосов
/ 13 августа 2011

Просто предположение: Classloader (и загруженные им классы) находятся не в обычной куче, а в постоянном поколении ("PermGen"). Эта часть памяти будет удаляться гораздо реже, чем все остальное, так как в нормальных условиях загрузчики классов не выходят из области видимости.

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

Зачем вам нужен сборщик загрузок?

0 голосов
/ 13 августа 2011

Вероятно, потому что JVM использует генеральную сборку мусора, но в конечном итоге будет выполнять полную разметку

...