Что на самом деле делает флаг JVM CMSClassUnloadingEnabled?

Я не могу на всю жизнь найти определение того, что действительно делает флаг Java CMSClassUnloadingEnabled, за исключением некоторых очень нечетких определений высокого уровня, таких как "избавляется от ваших проблем PermGen" (который он не поддерживает, btw).

Я просмотрел сайт Sun/Oracle и даже список опций фактически не говорит, что он делает.

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

Ответ 1

Обновление. Этот ответ относится к Java 5-7, у Java 8 это исправлено: https://blogs.oracle.com/poonam/about-g1-garbage-collector,-permanent-generation-and-metaspace Kudos перейдите в mt.uulu

Для Java 5-7:

Стандартный взгляд Oracle/Sun VM на мир: Классы навсегда. Поэтому, когда они загружены, они остаются в памяти, даже если никто больше не заботится. Обычно это не проблема, так как у вас нет такого количества чисто "настраиваемых" классов (= используется один раз для настройки, а затем никогда больше). Поэтому, даже если они занимают 1 МБ, кто заботится.

Но в последнее время у нас есть такие языки, как Groovy, которые определяют классы во время выполнения. Каждый раз, когда вы запускаете script, создается один (или более) новый класс, и они остаются навсегда в PermGen. Если вы используете сервер, это означает, что у вас есть утечка памяти.

Если вы включите CMSClassUnloadingEnabled, GC также развернет PermGen и удалит классы, которые больше не используются.

[EDIT] Вам также нужно включить UseConcMarkSweepGC (благодаря Sam Hasler). См. Этот ответ: fooobar.com/questions/5192/...

Ответ 2

Согласно сообщению в блоге Самый полный список -XX-параметров для Java JVM, он определяет, включена ли разгрузка классов под мусором CMS коллектор. По умолчанию используется false. Существует другая опция ClassUnloading, которая по умолчанию true, которая (предположительно) влияет на других сборщиков мусора.

Идея состоит в том, что если GC обнаруживает, что ранее загруженный класс больше не используется нигде в JVM, он может вернуть память, используемую для хранения байт-кода классов и/или собственного кода.

Настройка CMSClassUnloadingEnabled может помочь с вашей проблемой permgen, если вы в настоящее время используете сборщик CMS. Но есть вероятность, что вы не используете CMS или у вас есть подлинная утечка памяти, связанная с загрузкой класса. В последнем случае ваш класс никогда не появится в GC, который не будет использоваться... и поэтому никогда не будет выгружен.


Аарон Дигулла говорит, что "занятия навсегда". Это не совсем верно, даже в чисто Java-мире. Фактически, время жизни класса привязано к его загрузчику классов. Поэтому, если вы можете организовать загрузку класса загрузчика (и это не всегда легко сделать), то классы, которые он загрузил, также будут собраны в мусор.

Фактически, это то, что происходит, когда вы делаете горячую передислокацию webapp. (Или, по крайней мере, то, что должно произойти, если вы можете избежать проблем, которые приводят к утечке хранилища в пергеном.)

Ответ 3

Пример, где это полезно:

Настройка -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled в нашей Weblogic 10.3 JVM помогла решить проблему, когда реализация JAX-WS создала новый прокси-класс для каждого вызова веб-службы, что в конечном итоге привело к ошибкам памяти.

Не было тривиально проследить. Следующий код всегда возвращал один и тот же прокси-класс для port

final MyPortType port = 
Service.create(
        getClass().getResource("/path/to.wsdl"), 
        new QName("http://www.example.com", "MyService"))
    .getPort(
        new QName("http://www.example.com", "MyPortType"), 
        MyPortType.class);

Внутри этот прокси делегирован экземпляру weblogic.wsee.jaxws.spi.ClientInstance, который снова делегирован новому классу $Proxy[nnnn], где n был увеличен при каждом вызове. При добавлении флагов n все еще увеличивался, но по крайней мере те временные классы были удалены из памяти.

В более общем случае это может быть очень полезно при интенсивном использовании отражения Java и прокси-серверов через java.lang.reflect.Proxy