exception - thread - Tratar con el error "java.lang.OutOfMemoryError: PermGen"
java.lang.outofmemoryerror: permgen space tomcat (30)
Recientemente me encontré con este error en mi aplicación web:
java.lang.OutOfMemoryError: espacio PermGen
Es una aplicación típica de Hibernate / JPA + IceFaces / JSF que se ejecuta en Tomcat 6 y JDK 1.6. Al parecer, 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?
1) Aumentar el tamaño de la memoria PermGen
Lo primero que se puede hacer es aumentar el tamaño del espacio de almacenamiento dinámico de la generación permanente. Esto no se puede hacer con los argumentos JVM habituales –Xms (establecer tamaño de pila inicial) y –Xmx (configurar tamaño máximo de pila), ya que, como se mencionó, el espacio de pila permanente de la generación permanente es completamente independiente del espacio de pila de Java regular, y estos argumentos se establecen el espacio para este espacio de almacenamiento dinámico normal de Java. Sin embargo, hay argumentos similares que se pueden usar (al menos con los jvms Sun / OpenJDK) para aumentar el tamaño del montón de generación permanente:
-XX:MaxPermSize=128m
El valor predeterminado es 64m.
2) Habilitar el barrido
Otra forma de encargarse de eso es dejar que las clases se descarguen para que su PermGen nunca se agote:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
Cosas como esa funcionaron mágicamente para mí en el pasado. Una cosa, sin embargo, hay una importante compensación de rendimiento al usarlos, ya que los barridos de permgen realizarán como 2 solicitudes adicionales por cada solicitud que realice o algo por el estilo. Tendrá que equilibrar su uso con las compensaciones.
Puedes encontrar los detalles de este error.
http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html
- Abra tomcat7w desde el directorio bin de Tomcat o escriba Monitor Tomcat en el menú de inicio (se abre una ventana con pestañas con información de servicio diverso).
En el área de texto de Opciones Java, agregue esta línea:
-XX:MaxPermSize=128m
- Establezca el conjunto de memoria inicial en 1024 (opcional).
- Establezca la agrupación de memoria máxima en 1024 (opcional).
- Haga clic en Aceptar.
- Reinicie el servicio Tomcat.
"Ellos" están equivocados porque ejecuto 6.0.29 y tengo el mismo problema incluso después de configurar todas las opciones. Como Tim Howland dijo anteriormente, estas opciones solo aplazan lo inevitable. Me permiten volver a desplegar 3 veces antes de cometer el error en lugar de cada vez que lo vuelvo a desplegar.
Además, si está utilizando log4j en su aplicación web, consulte este párrafo en la documentation log4j.
Parece que si está utilizando PropertyConfigurator.configureAndWatch("log4j.properties")
, causa pérdidas de memoria cuando desinstala su aplicación web.
Alternativamente, puede cambiar a JRockit, cuyo manejo permgen de forma diferente a jvm del sol. En general, también tiene un mejor rendimiento.
http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html
Asignar más memoria a Tomcat NO es la solución adecuada.
La solución correcta es hacer una limpieza después de que el contexto se destruya y se vuelva a crear (la implementación en caliente). La solución es detener las fugas de memoria.
Si su Tomcat / Webapp Server le está diciendo que no pudo anular el registro de los controladores (JDBC), anule el registro. Esto detendrá las fugas de memoria.
Puede crear un ServletContextListener y configurarlo en su web.xml. Aquí hay un ejemplo de ServletContextListener:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.log4j.Logger;
import com.mysql.jdbc.AbandonedConnectionCleanupThread;
/**
*
* @author alejandro.tkachuk / calculistik.com
*
*/
public class AppContextListener implements ServletContextListener {
private static final Logger logger = Logger.getLogger(AppContextListener.class);
@Override
public void contextInitialized(ServletContextEvent arg0) {
logger.info("AppContextListener started");
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
logger.info("AppContextListener destroyed");
// manually unregister the JDBC drivers
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
logger.info(String.format("Unregistering jdbc driver: %s", driver));
} catch (SQLException e) {
logger.info(String.format("Error unregistering driver %s", driver), e);
}
}
// manually shutdown clean up threads
try {
AbandonedConnectionCleanupThread.shutdown();
logger.info("Shutting down AbandonedConnectionCleanupThread");
} catch (InterruptedException e) {
logger.warn("SEVERE problem shutting down AbandonedConnectionCleanupThread: ", e);
e.printStackTrace();
}
}
}
Y aquí lo configuras en tu web.xml:
<listener>
<listener-class>
com.calculistik.mediweb.context.AppContextListener
</listener-class>
</listener>
Aumentar el tamaño de la Generación Permanente o ajustar los parámetros del GC NO ayudará si tiene una pérdida de memoria real. Si su aplicación o alguna biblioteca de terceros que usa, filtra cargadores de clases, la única solución real y permanente es encontrar esta fuga y arreglarla. Existen varias herramientas que pueden ayudarlo, una de las recientes es Plumbr , que acaba de lanzar una nueva versión con las capacidades necesarias.
Dicen que la última versión de Tomcat (6.0.28 o 6.0.29) maneja la tarea de redistribuir los servlets mucho mejor.
El mensaje java.lang.OutOfMemoryError: PermGen
indica que el área de la Generación Permanente en la memoria está agotada.
Cualquier aplicación de Java puede usar una cantidad limitada de memoria. La cantidad exacta de memoria que su aplicación particular puede usar se especifica durante el inicio de la aplicación.
La memoria Java está separada en diferentes regiones que se pueden ver en la siguiente imagen:
Metaspace: Nace un nuevo espacio de memoria.
El JDK 8 HotSpot JVM ahora usa memoria nativa para la representación de metadatos de clase y se llama Metaspace; similar a Oracle JRockit e IBM JVM.
La buena noticia es que significa que no hay más problemas de espacio java.lang.OutOfMemoryError: PermGen
y que no es necesario que usted sintonice ni monitoree este espacio de memoria utilizando Java_8_Download o superior.
El primer paso en tal caso es verificar si el GC puede descargar clases de PermGen. La JVM estándar es bastante conservadora en este sentido: las clases nacen para vivir para siempre. Así que una vez cargadas, las clases permanecen en la memoria incluso si ningún código las está utilizando más. Esto puede convertirse en un problema cuando la aplicación crea dinámicamente muchas clases y las clases generadas no son necesarias por períodos más largos. En tal caso, puede ser útil permitir que la JVM descargue definiciones de clase. Esto se puede lograr agregando solo un parámetro de configuración a sus scripts de inicio:
-XX:+CMSClassUnloadingEnabled
De forma predeterminada, se establece en falso y, para habilitarlo, debe establecer explícitamente la siguiente opción en las opciones de Java. Si habilita CMSClassUnloadingEnabled, GC también barrerá PermGen y eliminará las clases que ya no se usen. Tenga en cuenta que esta opción solo funcionará cuando UseConcMarkSweepGC también esté habilitada mediante la opción a continuación. Por lo tanto, cuando ejecute ParallelGC o, Dios no lo permita, Serial GC, asegúrese de haber configurado su GC en CMS especificando:
-XX:+UseConcMarkSweepGC
En caso de que --launcher.XXMaxPermSize
esto en el IDE del eclipse, incluso después de configurar los parámetros --launcher.XXMaxPermSize
, -XX:MaxPermSize
, etc., aún si -XX:MaxPermSize
el mismo error, lo más probable es que el eclipse esté utilizando una versión con errores. de JRE que habría sido instalado por algunas aplicaciones de terceros y configurado por defecto. Estas versiones con errores no recogen los parámetros de PermSize, por lo que no importa lo que establezca, seguirá recibiendo estos errores de memoria. Entonces, en tu eclipse.ini agrega los siguientes parámetros:
-vm <path to the right JRE directory>/<name of javaw executable>
Asegúrese también de establecer el JRE predeterminado en las preferencias en el eclipse a la versión correcta de Java.
Estaba teniendo un problema similar. El mío es JDK 7 + Maven 3.0.2 + Struts 2.0 + proyecto basado en la inyección de dependencia GUICE de Google.
Cada vez que intenté ejecutar el mvn clean package
, se mostraba el siguiente error y "BUILD FAILURE" ocurrió
org.apache.maven.surefire.util.SurefireReflectionException: java.lang.reflect.InvocationTargetException; la excepción anidada es java.lang.reflect.InvocationTargetException: null java.lang.reflect.InvocationTargetException Causado por: java.lang.OutOfMemoryError: PermGen space
Probé todos los consejos y trucos útiles anteriores, pero desafortunadamente ninguno me funcionó. Lo que funcionó para mí se describe paso a paso a continuación: =>
- Ve a tu pom.xml
- Busque
<artifactId>maven-surefire-plugin</artifactId>
- Agregue un nuevo elemento
<configuration>
y luego<argLine>
sub elemento<argLine>
en el que pase-Xmx512m -XX:MaxPermSize=256m
como se muestra abajo =>
<configuration> <argLine>-Xmx512m -XX:MaxPermSize=256m</argLine> </configuration>
Espero que ayude, feliz programación :)
Intente -XX:MaxPermSize=256m
y si persiste, intente -XX:MaxPermSize=512m
La única forma en que funcionó para mí fue con la JVM JRockit. Tengo MyEclipse 8.6.
El montón de JVM almacena todos los objetos generados por un programa Java en ejecución. Java usa el new
operador para crear objetos, y la memoria para los nuevos objetos se asigna en el montón en tiempo de ejecución. La recolección de basura es el mecanismo de liberar automáticamente la memoria contenida por los objetos a los que ya no hace referencia el programa.
La configuración de la memoria depende de la naturaleza de su aplicación.
¿Qué estás haciendo?
¿Cuál es el importe de las transacciones precesadas?
¿Cuántos datos estás cargando?
etc.
etc.
etc
Probablemente podría crear un perfil de su aplicación y comenzar a limpiar algunos módulos de su aplicación.
Al parecer, esto puede ocurrir después de volver a implementar una aplicación varias veces
Tomcat tiene despliegue caliente pero consume memoria. Intenta reiniciar tu contenedor de vez en cuando. También necesitará saber la cantidad de memoria necesaria para ejecutar en modo de producción, esto parece ser un buen momento para esa investigación.
La respuesta más simple en estos días es usar Java 8.
Ya no reserva memoria exclusivamente para el espacio PermGen, lo que permite que la memoria PermGen se mezcle con el conjunto de memoria normal.
Tenga en cuenta que tendrá que eliminar todos los parámetros de inicio de JVM no estándar -XXPermGen...=...
si no desea que Java 8 se queje de que no hacen nada.
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, y luego ingresando al directorio Tomcat / bin y ejecutando tomcat6w.exe. Bajo la pestaña "Java", agregue los argumentos al cuadro "Opciones de Java". Haga clic en "Aceptar" y luego reinicie el servicio.
Si recibe un error, el servicio especificado no existe como un servicio instalado que debe ejecutar:
tomcat6w //ES//servicename
donde nombre_servicio es el nombre del servidor como se ve en services.msc
Fuente: comentario de orx sobre las respuestas ágiles de Eric .
Los errores comunes que las personas cometen son pensar que el espacio de almacenamiento dinámico y el espacio permanente son iguales, lo que no es del todo cierto. Podría tener mucho espacio restante en el montón, pero aún puede quedarse sin memoria en permgen.
Causas comunes de OutofMemory en PermGen es ClassLoader. Cada vez que se carga una clase en JVM, todos sus metadatos, junto con Classloader, se mantienen en el área de PermGen y se recolectarán como basura cuando el Classloader que los cargó esté listo para la recolección de basura. En Case Classloader tiene una pérdida de memoria que todas las clases cargadas por ella permanecerán en la memoria y causarán que el GenGen salga de la memoria una vez que lo repita un par de veces. El ejemplo clásico es Java.lang.OutOfMemoryError: PermGen Space en Tomcat .
Ahora hay dos maneras de resolver esto:
1. Encuentre la causa de la pérdida de memoria o si hay alguna pérdida de memoria.
2. Aumente el tamaño del espacio PermGen utilizando el -XX:MaxPermSize
JVM -XX:MaxPermSize
y -XX:PermSize
.
También puede consultar la Solución 2 de Java.lang.OutOfMemoryError en Java para obtener más detalles.
Los errores de PermGen del servidor de aplicaciones que ocurren después de múltiples implementaciones son probablemente causados por referencias mantenidas por el contenedor en los cargadores de clases de sus aplicaciones antiguas. Por ejemplo, el uso de una clase de nivel de registro personalizado hará que el cargador de clases del servidor de aplicaciones retenga las referencias. Puede detectar estas fugas en el cargador de clases utilizando las modernas herramientas de análisis JVM (JDK6 +) como jmap y jhat para ver qué clases continúan en su aplicación y rediseñando o eliminando su uso. Los sospechosos habituales son las bases de datos, los registradores y otras bibliotecas de nivel de marco de base.
Ver Fugas del cargador de clases: la excepción "java.lang.OutOfMemoryError: PermGen space" temida , y especialmente su publicación de seguimiento .
Me encuentro exactamente con el mismo problema, pero desafortunadamente ninguna de las soluciones sugeridas realmente funcionó para mí. El problema no sucedió durante la implementación, y tampoco estaba haciendo ninguna implementación en caliente.
En mi caso, el problema ocurrió cada vez en el mismo punto durante la ejecución de mi aplicación web, mientras se conectaba (a través de hibernación) a la base de datos.
Este enlace (también mencionado anteriormente) proporcionó información suficiente para resolver el problema. Mover el controlador jdbc- (mysql) fuera de la WEB-INF y en la carpeta jre / lib / ext / parece haber resuelto el problema. Esta no es la solución ideal, ya que la actualización a un JRE más reciente requiere que vuelva a instalar el controlador. Otro candidato que podría causar problemas similares es log4j, por lo que es posible que desee mover ese también.
Probé varias respuestas y lo único que finalmente hizo el trabajo fue esta configuración para el complemento del compilador en el pom:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<fork>true</fork>
<meminitial>128m</meminitial>
<maxmem>512m</maxmem>
<source>1.6</source>
<target>1.6</target>
<!-- prevent PermGen space out of memory exception -->
<!-- <argLine>-Xmx512m -XX:MaxPermSize=512m</argLine> -->
</configuration>
</plugin>
Espero que esto ayude.
Se produce un error en el espacio de generación de permisos debido al uso de espacio grande en lugar de que jvm proporcionó espacio para ejecutar el código. La mejor solución para este problema en los sistemas operativos UNIX es cambiar alguna configuración en el archivo bash. Los siguientes pasos resuelven el problema.
Ejecute el comando gedit .bashrc
en el terminal.
Crea JAVA_OTPS
variable JAVA_OTPS
con el siguiente valor:
export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"
Guarde el archivo bash. Ejecute el comando exec bash en el terminal. Reinicie el servidor.
Espero que este enfoque funcione en su problema. Si utiliza una versión de Java inferior a 8, este problema ocurre a veces. Pero si usas Java 8 el problema nunca ocurre.
Será mejor que intentes -XX:MaxPermSize=128M
lugar de -XX:MaxPermGen=128M
.
No puedo decir el uso preciso de este grupo de memoria, pero tiene que ver con el número de clases cargadas en la JVM. (Por lo tanto, habilitar la descarga de clases para Tomcat puede resolver el problema). Si sus aplicaciones generan y compilan clases en la ejecución, es más probable que necesiten una agrupación de memoria más grande que la predeterminada.
Set -XX:PermSize=64m -XX:MaxPermSize=128m
. Más adelante también puede intentar aumentar MaxPermSize
. Espero que funcione. Lo mismo funciona para mí. Establecer solo MaxPermSize
no funcionó para mí.
También he estado luchando contra este problema al implementar y desinstalar una aplicación web compleja, y pensé que agregaría una explicación y mi solución.
Cuando implemento 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 en la desinstalación, todo se supone que debe desaparecer muy 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á cargada están aquí para quedarse.
Y después de un par de redespliegues, nos encontramos con el OutOfMemoryError.
Ahora esto se ha convertido en un problema bastante serio. Podría asegurarme de que Tomcat se reinicie después de cada redespliegue, pero eso elimina todo el servidor, en lugar de solo la aplicación que se está redistribuyendo, lo que a menudo no es factible.
Entonces, en lugar de eso, he reunido 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 recalcar 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 se puede cambiar el código existente para usar los métodos adecuados de cierre y limpieza . La única vez que se debe usar es si hay una biblioteca externa de la que depende su código (en mi caso, era un cliente RADIUS) que no proporciona un medio para limpiar sus propias referencias estáticas.
De todos modos, con el código. Esto debe llamarse en el punto donde la aplicación no se está desplegando, como el método de destrucción de un servlet o (el mejor enfoque) el método contextDestroyed de ServletContextListener.
//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();
Tengo una combinación de Hibernate + Eclipse RCP, intenté usar -XX:MaxPermSize=512m
y -XX:PermSize=512m
y parece estar funcionando para mí.
Tuve el problema del que estamos hablando aquí, mi escenario es eclipse-helios + tomcat + jsf y lo que estabas haciendo es hacer una implementación sencilla de Tomcat. Estaba mostrando el mismo problema aquí, resuelto como sigue.
En eclipse, vaya a la pestaña de servidores, haga doble clic en el servidor registrado en mi caso tomcat 7.0, se abre la información de registro general de mi servidor de archivos. En la sección "Información general", haga clic en el enlace "Abrir configuración de inicio" , esto abre la ejecución de las opciones del servidor en la pestaña Argumentos en los argumentos de VM agregados al final de estas dos entradas
-XX: MaxPermSize = 512m
-XX: PermSize = 512m
y listo.
Utilice el parámetro de línea de comando -XX:MaxPermSize=128m
para una Sun JVM (obviamente, sustituyendo 128 por cualquier tamaño que necesite).
-XX: MaxPermSize = 128m
(puede experimentar lo que mejor funciona) a VM Arguments, ya que estoy usando eclipse ide. En la mayoría de JVM, el PermSize predeterminado es de alrededor de 64 MB, que se queda sin memoria si hay demasiadas clases o un gran número de cadenas en el proyecto.
Para eclipse, también se describe en la answer .
PASO 1 : Haga doble clic en el servidor tomcat en la pestaña Servidores
PASO 2 : Abra el inicio Conf y agregue -XX: MaxPermSize = 128m
al final de los argumentos de VM existentes.
jrockit resolvió esto para mí también; sin embargo, noté que los tiempos de reinicio del servlet fueron mucho peores, por lo que si bien fue mejor en producción, fue una especie de obstáculo en el desarrollo.