jueves, 27 de enero de 2011

java.lang.OutOfMemoryError: PermGen

En algunas ocasiones tenemos problemas con el espacio de memoria permanente en nuestras aplicaciones Java. Durante la ejecución d cualquier método o servicio la aplicación lanza una excepción java.lang.OutOfMemoryError: PermGen. Esto sucede porque el espacio de memoria Permanente (no es el Heap que se usa  para asignar los objetos), que se usa para colocar contenido generado por la aplicación, como las clases que carga el ClassLoader o las cadenas internas (String.intern), que se usan para optimizar el manejo de las String, se llena y se queda sin espacio. El contenido cargado en este área de memoria no se libera (por defecto).

Sucede habitualmente durante los reinicios en caliente de los servidores de aplicaciones, ya que se producen fugas de memoria al reiniciar el servidor sin apagarlo, es decir como no se apaga no se libera la memoria permanente usada, y tras múltiples reinicios se llena. La solución obvia  en este caso es reiniciar.

También puede suceder por otras circunstancias, como que realmente se esté llenando el espacio de memoria permanente. La solución trivial es aumentar el tamaño de este espacio de memoria, para ello modificaremos las opciones de la Máquina Virtual de Java (JVM) usando la variable de entorno JAVA_OPTS aumentaremos el tamaño máximo con:

 -XX:MaxPermSize=256m

Esta solución puede solucionar el problema, si colocamos el tamaño adecuado, pero si aun así el espacio es insuficiente tan sólo lo retrasaremos. Para ello deberíamos saber cuánta memoria esta usando nuestra aplicación. Para saber cuánta memoria usa la aplcacion debemos añadir en JAVA_OPTS lo siguiente:

            -verbose:gc -XX:+PrintGCDetails

Con los valores anteriores indicamos a la JVM que se muestre los datos de recolector de basura y que muestre los detalles, algo así:

[Full GC [CMS: 168674K->168674K(379016K), 0.9549626 secs] 168688K->168674K(395336K), [CMS Perm : 72024K->72024K(122292K)], 0.9556050 secs]

El texto indicado tiene el siguiente patrón:

[CMS Perm : Memoria Antes(Kb)->Memoria después(Kb)(Memoria en uso(Kb))]

Con lo cuál veremos, cada vez que se ejecute el recolector de basura la memoria en uso, y sabremos que tamaño debemos poner al espacio PermGen.

Una solución mejor es permitir  el recolector de basura que elimine el contenido de la memoria PermGen que no esté en uso, hay muchas clases que se cargan y se quedan ahí sin usarlas más (esto sucede con asiduidad si usamos frameworks como Hibernate o Spring). Para ello debemos añadir a la variable JAVA_OPTS lo siguiente:

  • -XX:+UseConcMarkSweepGC --> Política del garbageCollector, para que trabaje de forma concurrente si hay múltiples procesadores.
  • -XX:+CMSPermGenSweepingEnabled --> Se habilita el barrido por el espacio de memoria permanente (PermGen).
  • -XX:+CMSClassUnloadingEnabled --> Se permite descargar clases que no se usen.


NOTA IMPORTANTE: Si estamos usando Maven para lanzar nuestro servidor de aplicaciones la variable de entrono que debemos modificar es MAVE_OPTS en lugar de JAVA_OPTS.