Causas y solución de java.lang.OutOfMemoryError: PermGen space failure

28 de marzo de 2008

La memoria de la Máquina Virtual se divide en varias regiones. Una de estas regiones es el PermGen, el área de memoria utilizada para, entre otras cosas, guardar el metadata de las clases como los atributos y sus tipos de datos, métodos etc. Esta memoria es de tipo non-heap. Las instancias de las clases se cargan en la memoria de tipo heap, a la que se van añadiendo y eliminando las instancias de las clases según se van utilizando y eliminándose por el recolector de basura (Garbage Collector, en adelante GC).

No vamos a entrar en el mecanismo de la carga y eliminación de clases en java porque excede este artículo, pero sí lo comentaremos brevemente para poder entender el porqué de la excepción OutOfMemoryException: para que una clase pueda ser eliminada por el recolector de basura (GC), es necesario que no esté referenciada por ninguna otra clase. Para que se pueda eliminar la memoria ocupada por una clase en el espacio PermGen, es necesario además que se elimine el classloader que cargó la clase.

El valor por defecto del espacio PermGen es de 64 Mb en la máquina virtual (VM) de Sun. Este valor es suficiente normalmente para las aplicaciones que corren de forma independiente. En el caso de una aplicación que corra en un servidor web tipo Tomcat o un servidor de aplicaciones, hay casos en los que este valor no es suficiente.

En primer lugar, es posible que la aplicación necesite más espacio por su propia arquitectura. Las librerías Spring e Hibernate son librerías grandes, que cargan muchas clases y además hacen uso de proxies y carga dinámica de clases que hacen uso del espacio PermGen por lo que puede que los 64Mb no sean suficientes. Este caso se da cuando la excepción con el error se produce nada mas arrancar el servidor web o de aplicaciones o al acceder a la aplicación. Para solventar este problema bastará con aumentar el tamaño máximo de la memoria de tipo PermGen según veremos más adelante.

En segundo lugar, la causa más probable de una excepción java.lang.OutOfMemoryError: PermGen space failure se produce cuando la aplicación se reinstala en el servidor sin reiniciar el servidor. La forma de reinstalar las aplicaciones es eliminando el classloader que cargó la aplicación por primera vez y creando un nuevo classloader para la nueva instancia de la aplicación. En teoría, esto proporciona una forma limpia de reinstalar aplicaciones, pero si el servidor web o de aplicaciones guarda una referencia al classloader antiguo, se produce una fuga de memoria (memory leak) y el resultado es que tendremos las clases de la aplicación cargadas dos veces en memoria. Es cuestión del número de recargas y de la memoria gastada por la aplicación el que se produzca la excepción  java.lang.OutOfMemoryError: PermGen space failure. En este caso no basta con aumentar el tamaño máximo de la memoria, ya que esto sólo retrasaría el problema a unas cuantas reinstalaciones más. En este caso la única solución consiste en averiguar la causa de la fuga de memoria. Esto también excede el contenido de este artículo, aunque lo intentarmos tratar con profundidad en otra ocasión. Por el momento sólo proporcionamos un enlace muy interesante, aunque en inglés, acerca de posibles causas de fugas de memoria en java: http://opensource.atlassian.com/confluence/spring/pages/viewpage.action?pageId=2669

Por último, a continuación incluimos el argumento que es necesario incluir al arrancar la máquina virtual para especificar el tamaño de esta región PermGen en Mb, XX:MaxPermSize=128m para el tamaño máximo y XX:PermSize=128m para que la máquina virtual ya arranque con este tamaño y no tenga que reservar más espacio en tiempo de ejecución.