2011-11-05 11 views
9

Trabajo en una aplicación Tomcat que utiliza el colector CMS junto con una barra de memoria para activar GC. Cuando vuelvo a cargar las webapps, a veces termino en una situación en la que el viejo generador está lo suficientemente lleno como para activar el GC, pero los cargadores de clase muertos no se recogen.¿Cuándo se recoge el gen de la permanente?

Leí que las clases se asignan al generador de perm e intuí que, por lo tanto, las antiguas colecciones de gen estaban ignorando. Escribí la siguiente clase de prueba para probar esta teoría.

package test; 

import java.io.IOException; 
import java.io.InputStream; 

import org.apache.commons.io.IOUtils; 

/* 
JVM Options: 
-server -XX:+UseMembar -XX:+UseConcMarkSweepGC 
-XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=80 
-XX:+UseCMSInitiatingOccupancyOnly -Xms100m -Xmx100m 
-XX:PermSize=100m -XX:NewSize=10m -XX:MaxNewSize=10m 
-verbose:gc -Xloggc:gc.log -XX:+PrintGCTimeStamps 
-XX:+PrintGCDetails 
*/ 
public class ClassLoaderTest extends ClassLoader 
{ 
    @Override 
    protected synchronized Class<?> loadClass(String xiName, boolean xiResolve) 
     throws ClassNotFoundException 
    { 
    if (xiName.equals("test")) 
    { 
     // When asked to load "test", load Example.class 
     Class<?> c = Example.class; 
     String className = c.getName(); 
     String classAsPath = className.replace('.', '/') + ".class"; 
     InputStream stream = c.getClassLoader().getResourceAsStream(classAsPath); 
     byte[] classData = null; 
     try 
     { 
     classData = IOUtils.toByteArray(stream); 
     } 
     catch (IOException e) 
     { 
     e.printStackTrace(); 
     } 
     return defineClass(className, classData, 0, classData.length); 
    } 
    return super.loadClass(xiName, xiResolve); 
    } 

    public static class Example {} 
    public static ClassLoaderTest classLoaderTest; 

    public static void main(String[] args) throws Exception 
    { 
    // Allocate CL in new gen 
    classLoaderTest = new ClassLoaderTest(); 

    // Load a class - allocated in perm gen 
    classLoaderTest.loadClass("test"); 

    // Discard CL 
    classLoaderTest = null; 

    // Pause at end 
    Thread.sleep(99 * 60 * 1000); 
    } 

    public final byte[] mMem = new byte[85 * 1024 * 1024]; 
} 

que corrió esta clase y supervisa la salida usando VisualVM y vio que había hecho varias colecciones antiguas y generación joven que suceden sin el cargador de clase muertos siendo recogidos y por ello la gran matriz de bytes permanecieron en la memoria.

VisualVM Visual GC

Lo que daría lugar a la ondulación permanente Gen a cobrar?

+0

¿Sabía que "Perm" es la abreviatura de [... ¿correcto?] (Http://en.wiktionary.org/wiki/permanent#Adjective) –

+4

La memoria asignada en Java Perm Gen aún se puede recopilar. La JVM usa Perm Gen para contener datos que espera que se liberen muy raramente. – mchr

Respuesta

4

Consulte las opciones de JVM "CMSPermGenSweepingEnabled" y "CMSClassUnloadingEnabled". (Hay muchas discusiones en la red sobre su uso). En particular, el primero supone incluir el PermGen en una ejecución de recolección de basura. Por defecto, el espacio PermGen nunca se incluye en la recolección de basura (y por lo tanto crece sin límites).

+1

No crece sin límites, hay un límite superior muy definido. Es por eso que a menudo obtienes la temida fuga del cargador de clases: https://blogs.oracle.com/fkieviet/entry/classloader_leaks_the_dreaded_java –

+0

@Cameron Skinner + ¡Punto tomado! Intenta crecer sin tener en cuenta MaxPermSize :-) – mazaneicha

+1

Intenté agregar el -XX: + CMSPermGenSweepingEnabled -XX: + CMSClassUnloadingEnabled en mis archivos JVM. Descubrí que solo -XX: + CMSClassUnloadingEnabled realmente hacía algo. También me sorprendió descubrir que el gen de la permanente solo se recogió después de 2 minutos poco después de la segunda colección de gen jóvenes. – mchr

Cuestiones relacionadas