¿Qué hace realmente la bandera JVM CMSClassUnloadingEnabled?

183

Por mi vida, no puedo encontrar una definición de lo que CMSClassUnloadingEnabledrealmente hace el indicador Java VM , aparte de algunas definiciones de alto nivel muy difusas como "deshacerse de sus problemas de PermGen" ( que no es , por cierto).

He buscado en el sitio de Sun / Oracle, e incluso la lista de opciones en realidad no dice lo que hace.

Según el nombre de la bandera, supongo que el recolector de basura CMS no descarga las clases de manera predeterminada, y esta bandera lo enciende, pero no puedo estar seguro.

Rico
fuente

Respuestas:

219

Actualización Esta respuesta es relevante para Java 5-7, Java 8 tiene esto solucionado: https://blogs.oracle.com/poonam/about-g1-garbage-collector,-permanent-generation-and-metaspace Kudos van a mt. uulu

Para Java 5-7:

El aspecto estándar de Oracle / Sun VM en el mundo es: Las clases son para siempre. Entonces, una vez cargados, permanecen en la memoria incluso si a nadie le importa más. Esto generalmente no es un problema ya que no tiene tantas clases puramente de "configuración" (= usado una vez para la configuración y luego nunca más). Entonces, incluso si ocupan 1 MB, a quién le importa.

Pero últimamente, tenemos idiomas como Groovy, que definen clases en tiempo de ejecución. Cada vez que ejecuta un script, se crean una (o más) nuevas clases y permanecen en PermGen para siempre. Si está ejecutando un servidor, eso significa que tiene una pérdida de memoria.

Si habilita CMSClassUnloadingEnabledel GC, también barrerá PermGen y eliminará las clases que ya no se usan.

[EDITAR] También tendrás que habilitar UseConcMarkSweepGC(gracias a Sam Hasler ). Vea esta respuesta: https://stackoverflow.com/a/3720052/2541

Aaron Digulla
fuente
17
De acuerdo con stackoverflow.com/a/3720052/2541 para CMSClassUnloadingEnabledtener algún impacto, UseConcMarkSweepGCtambién se debe configurar
Sam Hasler
1
No estoy seguro de cómo esto afecta la idea de usar UseConcatSweepGC, pero parece que recientemente se corrigió un error en CMSClassUnloadingEnabled. Se comenta
Bill Rosmus
3
@ Kevin: Sí, definitivamente. Vea al final de groovy.codehaus.org/Running : "Groovy crea clases dinámicamente, pero la máquina virtual Java predeterminada no GC el PermGen. Si está utilizando Java 6 o posterior, se necesita agregar -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC. UseConcMarkSweepGCPara habilitar CMSClassUnloadingEnabled".
Aaron Digulla
1
Un buen artículo sobre el uso de UseConcMarkSweepGC y CMSClassUnloadingEnabled juntos. blog.redfin.com/devblog/2012/06/…
Victor
1
ya no es válido para 1.8: blogs.oracle.com/poonam/…
mt.uulu
35

De acuerdo con la publicación del blog La lista más completa de opciones -XX para Java JVM , determina si la descarga de clases está habilitada bajo el recolector de basura CMS. El valor por defecto es false. Hay otra opción llamada ClassUnloadingque es truepor defecto que (presumiblemente) afecta a los otros recolectores de basura.

La idea es que si el GC detecta que una clase cargada previamente ya no se usa en ninguna parte de la JVM, puede recuperar la memoria utilizada para contener el bytecode de las clases y / o el código nativo.

Configurar CMSClassUnloadingEnabled podría ayudarlo con su problema de permgen si actualmente está utilizando el recopilador de CMS . Pero lo más probable es que no esté utilizando el CMS o que tenga una pérdida de memoria genuina relacionada con el cargador de clases. En el último caso, su clase nunca aparecerá en el GC como no utilizada ... y, por lo tanto, nunca se descargará.


Aaron Digulla dice "las clases son para siempre". Esto no es estrictamente cierto, incluso en el mundo puramente Java. De hecho, la vida útil de una clase está vinculada a su cargador de clases. Entonces, si puede organizar que un cargador de clases sea basura recolectada (y eso no siempre es algo fácil de hacer), las clases que cargó también serán recolectadas.

De hecho, esto es lo que sucede cuando realizas una nueva implementación de una aplicación web. (O al menos, eso es lo que debería suceder, si puede evitar los problemas que conducen a una fuga de almacenamiento de permgen).

Stephen C
fuente
24

Un ejemplo donde esto es útil:

La configuración -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabledde nuestra JVM de Weblogic 10.3 ayudó a resolver un problema en el que la implementación JAX-WS creó una nueva clase de proxy para cada llamada al servicio web, lo que eventualmente condujo a errores de memoria insuficiente.

No fue trivial rastrear. El siguiente código siempre devolvió la misma clase de proxy paraport

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);

Internamente, este proxy delega en una instancia de weblogic.wsee.jaxws.spi.ClientInstance, que nuevamente delega en una nueva $Proxy[nnnn]clase donde nse incrementa en cada llamada. Al agregar las banderas, ntodavía se incrementó, pero al menos esas clases temporales se eliminaron de la memoria.

En una nota más general, esto puede ser muy útil cuando se hace un uso intensivo de la reflexión y los proxies de Java a través de java.lang.reflect.Proxy

Lukas Eder
fuente
+1 para compartir experiencias reales. También tuvimos este problema en torquebox donde el servidor generó una gran cantidad de clases debido a los procesos de compilación de JRuby.
nurettin
77
También tenga en cuenta que -XX:+CMSPermGenSweepingEnabledestá en desuso a favor de-XX:+CMSClassUnloadingEnabled
nurettin
3
Sin embargo, una solución real para este problema es crear el puerto una vez y reutilizarlo. Así es como se supone que se debe usar JAX-WS. El puerto también es 100% seguro para subprocesos.
rustyx
@rustyx: ¿Puedes respaldar esa afirmación con algún enlace autorizado? Siempre he sido muy cauteloso con la reutilización de proxies y stubs de servicios web ... Por ejemplo, vea los descargos de responsabilidad de Apache CXF . O esta pregunta
Lukas Eder
1
@rukavitsya: Como dije en mi respuesta. Cada vez que llamé a la lógica anterior, se creó un nuevo proxy
Lukas Eder