2011-08-12 3 views
9

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

Вот сумасшедшая часть ... и я обнаружил это случайно.

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

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

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

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

ВС 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) 
+0

Это может быть полезно: http://java.sun.com/developer/technicalArticles/javase/finalization/ – Jeremy

+0

Возможно, еще один бит информации имеет значение: здесь я использую параллельный сборщик. – Scott

+0

Мой опыт совсем такой же, как у вас, и я всегда мог только объяснить это так, как надлежащий GC является сложной задачей. Интересная «дискуссия» об этом идет здесь: http://stackoverflow.com/questions/176745/circular-references-in-java. Есть некоторые особые случаи, которые обрабатывает сборщик мусора, один из которых является Class Loaders, а другие - слабыми ссылками. Если вам случится это доказать, прочитав какой-то фактический исходный код GC или _proven_ answer, отправьте его здесь. Еще одна статья, подробно обсуждающая GC в Java5: http://www.oracle.com/technetwork/java/gc-tuning-5-138395.html –

ответ

0

Скорее всего потому, что виртуальная машина использует generational сбора мусора, но в конечном итоге будет делать полный отметь и подметать

+0

Если я не понимаю смысл этих терминов, я вижу, как PS MarkSweep major счётчик счетчиков коллекций (и, как следствие, снижение использования кучи кучного использования), мне кажется, что полная развертка происходит, когда я ее просил. – Scott

0

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

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

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

+0

С риском упрощения нашей потребности в GC'in CL есть «подключаемые модули», которые могут входить и уходить в приложение, которое вносит код, конфигурацию и другие ресурсы. Мы создаем и используем специальный загрузчик классов для этих модулей для достижения наших требований, но когда модуль идет, мы хотим восстановить память, которую он потреблял, в том числе permgen. – Scott

+0

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

5

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

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

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

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

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

+0

Может быть правильно ... но я не могу доказать это с очевидным недетерминированным поведением, которое я вижу. В конце концов, мой CL собирается, и я даже вижу падение использования пергмена, что хорошо. Я просто не люблю недетерминированное поведение, особенно когда оно может вернуться и укусить меня позже от производственной проблемы! :) – Scott

+0

@Scott: К сожалению, настоящая сборка мусора всегда не детерминирована. Ты действительно ничего не можешь с этим поделать; если вам действительно нужно детерминированное поведение, вы должны изучить [Java в реальном времени] (http://en.wikipedia.org/wiki/Real-time_specification_for_Java). –

0

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

-XX:SoftRefLRUPolicyMSPerMB=1 

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

Смежные вопросы