Pregunta Tratando con el error "java.lang.OutOfMemoryError: PermGen space"


Recientemente me encontré con este error en mi aplicación web:

java.lang.OutOfMemoryError: espacio PermGen

Es una aplicación Hibernate / JPA + IceFaces / JSF típica que se ejecuta en Tomcat 6 y JDK 1.6. Aparentemente esto puede ocurrir después de volver a implementar una aplicación varias veces.

¿Qué lo causa y qué se puede hacer para evitarlo? ¿Cómo soluciono el problema?


1178
2017-09-18 03:29


origen


Respuestas:


La solución fue agregar estos indicadores a la línea de comandos de JVM cuando se inicia Tomcat:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Puede hacerlo cerrando el servicio Tomcat, luego ingresando en el directorio Tomcat / bin y ejecutando tomcat6w.exe. En la pestaña "Java", agrega los argumentos al cuadro "Opciones de Java". Haga clic en "Aceptar" y luego reinicie el servicio.

Si obtiene un error el servicio especificado no existe como un servicio instalado deberías correr:

tomcat6w //ES//servicename

dónde Nombre del Servicio es el nombre del servidor como se ve en services.msc

Fuente: comentario de orx en Respuestas ágiles de Eric.


549
2017-09-17 22:23



Es mejor que intentes -XX:MaxPermSize=128M más bien que -XX:MaxPermGen=128M.

No puedo decir el uso preciso de este conjunto de memoria, pero tiene que ver con el número de clases cargadas en la JVM. (Por lo tanto, habilitar la descarga de clase para tomcat puede resolver el problema). Si sus aplicaciones generan y compilan clases en la ejecución, es más probable que necesiten un grupo de memoria más grande que el predeterminado.


247
2017-09-18 02:09



Los errores de PermGen del servidor de aplicaciones que suceden después de varias implementaciones probablemente se deben a referencias guardadas por el contenedor en los cargadores de clases de las aplicaciones anteriores. Por ejemplo, el uso de una clase de nivel de registro personalizado hará que el cargador de clases del servidor de la aplicación retenga las referencias. Puede detectar estas fugas entre clasificadores utilizando herramientas de análisis de JVM modernas (JDK6 +), como jmap y jhat, para ver qué clases se conservan en su aplicación y rediseñar o eliminar su uso. Los sospechosos habituales son bases de datos, registradores y otras bibliotecas de nivel de marco base.

Ver Fugas de Classloader: la temida excepción "java.lang.OutOfMemoryError: PermGen space", y especialmente su publicación de seguimiento.


152
2018-03-11 22:24



Los errores comunes que comete la gente es pensar que el espacio en montón y el espacio permgen son los mismos, lo cual no es del todo cierto. Puede tener mucho espacio restante en el montón, pero aún puede quedarse sin memoria en permgen.

Las causas comunes de OutofMemory en PermGen son ClassLoader. Siempre que una clase se carga en la máquina virtual Java, todos sus metadatos, junto con cargador de clases, se mantiene en el área PermGen y que será el recolector de basura cuando el cargador de clases que les cargado está listo para la recolección de basura. En caso de que el cargador de clases tenga una pérdida de memoria, todas las clases cargadas permanecerán en la memoria y hará que permGen salga de la memoria una vez que la repita un par de veces. El ejemplo clásico es Java.lang.OutOfMemoryError: PermGen Space en Tomcat.

Ahora hay dos formas de resolver esto:
1. Encuentre la causa de la pérdida de memoria o si hay alguna pérdida de memoria.
2. Incremente el tamaño de PermGen Space utilizando el param de JVM -XX:MaxPermSize y -XX:PermSize.

También puedes consultar 2 Solución de Java.lang.OutOfMemoryError en Java para más detalles.


65



Usa el parámetro de línea de comando -XX:MaxPermSize=128m para una Sun JVM (obviamente sustituyendo 128 para cualquier tamaño que necesite).


39



Tratar -XX:MaxPermSize=256m y si persiste, prueba -XX:MaxPermSize=512m


35



yo adicional  -XX: MaxPermSize = 128m (puedes experimentar cuál funciona mejor) para Argumentos VM como estoy usando eclipse ide. En la mayoría de JVM, predeterminado PermSize esta alrededor 64 MB que se queda sin memoria si hay demasiadas clases o una gran cantidad de cadenas en el proyecto.

Para eclipse, también se describe en responder.

PASO 1 : Haga doble clic en el servidor Tomcat en Servidores Lengüeta

enter image description here

PASO 2 : Abrir lanzamiento Conf y añadir -XX: MaxPermSize = 128m hasta el final de existir Argumentos VM.

enter image description here


24



Me he estado tomando el pelo contra este problema al desplegar y desinstalar una aplicación web compleja, y pensé en agregar una explicación y mi solución.

Cuando despliegue una aplicación en Apache Tomcat, se crea un nuevo ClassLoader para esa aplicación. El ClassLoader luego se usa para cargar todas las clases de la aplicación, y al anular la implementación, se supone que todo va bien. Sin embargo, en realidad no es tan simple.

Una o más de las clases creadas durante la vida de la aplicación web contiene una referencia estática que, en algún punto de la línea, hace referencia al ClassLoader. Como la referencia es originalmente estática, ninguna cantidad de recolección de basura limpiará esta referencia; el ClassLoader y todas las clases que está cargando están aquí para quedarse.

Y después de un par de redespliegues, encontramos el OutOfMemoryError.

Ahora esto se ha convertido en un problema bastante serio. Podría asegurarme de que Tomcat se reinicie después de cada redistribución, pero eso anula todo el servidor, y no solo la aplicación que se redistribuye, lo que a menudo no es factible.

Entonces, en su lugar, he creado una solución en código, que funciona en Apache Tomcat 6.0. No he probado en ningún otro servidor de aplicaciones, y debo enfatizar que Es muy probable que esto no funcione sin modificaciones en ningún otro servidor de aplicaciones.

También me gustaría decir que personalmente odio este código, y que nadie debería usar esto como una "solución rápida" si el código existente se puede cambiar para usar el apagado adecuado y los métodos de limpieza. El único momento en que esto debería usarse es si hay una biblioteca externa de la que su código es dependiente (en mi caso, era un cliente RADIUS) que no proporciona un medio para limpiar sus propias referencias estáticas.

De todos modos, sigue con el código. Esto debe invocarse en el punto donde la aplicación no se está implementando, como el método de destrucción de un servlet o (el mejor enfoque) el método de contexto de ServletContextListenerDestroyed.

//Get a list of all classes loaded by the current webapp classloader
WebappClassLoader classLoader = (WebappClassLoader) getClass().getClassLoader();
Field classLoaderClassesField = null;
Class clazz = WebappClassLoader.class;
while (classLoaderClassesField == null && clazz != null) {
    try {
        classLoaderClassesField = clazz.getDeclaredField("classes");
    } catch (Exception exception) {
        //do nothing
    }
    clazz = clazz.getSuperclass();
}
classLoaderClassesField.setAccessible(true);

List classes = new ArrayList((Vector)classLoaderClassesField.get(classLoader));

for (Object o : classes) {
    Class c = (Class)o;
    //Make sure you identify only the packages that are holding references to the classloader.
    //Allowing this code to clear all static references will result in all sorts
    //of horrible things (like java segfaulting).
    if (c.getName().startsWith("com.whatever")) {
        //Kill any static references within all these classes.
        for (Field f : c.getDeclaredFields()) {
            if (Modifier.isStatic(f.getModifiers())
                    && !Modifier.isFinal(f.getModifiers())
                    && !f.getType().isPrimitive()) {
                try {
                    f.setAccessible(true);
                    f.set(null, null);
                } catch (Exception exception) {
                    //Log the exception
                }
            }
        }
    }
}

classes.clear();

22