java - parametro - Pérdida de memoria al redistribuir la aplicación en Tomcat
subir memoria a tomcat (6)
La pérdida de memoria en la redistribución de Tomcat es un problema muy antiguo. La única forma real de resolverlo es reiniciar Tomcat en lugar de volver a implementar la aplicación. Si tiene varias aplicaciones, necesita ejecutar los servicios de varios tomcat en diferentes puertos y unirse a ellos con nginx.
Tengo aplicación web que se implementa en Tomcat 7.0.70. Simulé la siguiente situación:
- He creado el volcado de pila.
- Luego envié la solicitud Http y en el método de servicio imprimí el hilo actual y su classLoader. Y luego invocé Thread.currentThread.sleep (10000).
- Y, al mismo tiempo, hice clic en "anular la implementación de esta aplicación" en la página de administración de Tomcat.
- He creado un nuevo volcado de pila.
- Después de unos minutos creé un nuevo volcado hepático.
RESULTADOS
Volcado de hilo
En la siguiente pantalla puede ver que después de hacer clic en "volver a desplegar", se eliminaron todos los subprocesos (que estaban asociados con esta aplicación web) excepto el subproceso "http-apr-8081-exec-10". Cuando establecí el atributo de Tomcat "renewThreadsWhenStoppingContext == true", así puedes ver que después de un tiempo este hilo ("http-apr-8081-exec-10") fue eliminado y un nuevo hilo (http-apr-8081-exec-11 ) fue creado en lugar de él. Así que no esperaba tener el antiguo WCL después de la creación del volcado de pila 3, porque no hay ningún hilo u objeto antiguo.
Volcado Heapd 1
En las siguientes dos pantallas puede ver que cuando la aplicación se estaba ejecutando solo había una WCL (su parámetro "start" = true). Y el hilo "http-apr-8081-exec-10" tenía el contextClassLoader = URLClassLoader (porque estaba en el grupo de Tomcat). Solo hablo de este hilo porque podrás ver que este hilo manejará mi futura solicitud HTTP.
Enviando solicitud HTTP
Ahora envío la solicitud HTTP y en mi código obtengo información sobre el subproceso actual. Puede ver que mi solicitud está siendo manejada por el subproceso "http-apr-8081-exec-10"
дек 23, 2016 9:28:16 AM c.c.c.f.s.r.ReportGenerationServiceImpl INFO: request has been handled in
thread = http-apr-8081-exec-10, its contextClassLoader = WebappClassLoader
context: /hdi
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader: java.net.URLClassLoader@4162ca06
Luego hago clic en "Reimplementar mi aplicación web" y aparece el siguiente mensaje en la consola.
дек 23, 2016 9:28:27 AM org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
SEVERE: The web application [/hdi] appears to have started a thread named [http-apr-8081-exec-10] but has failed to stop it. This is very likely to create a memory leak.
Volcado Heapd 2
En las siguientes pantallas puede ver que hay dos instancias WebAppClassLoader. Uno de ellos (número # 1) es antiguo (su atributo "iniciado" = falso). Y la WCL # 2 se creó después de volver a implementar la aplicación (su atributo "start" = true). Y el hilo que revisamos tiene contextClassLoader = "org.apache.catalina.loader.WebappClassLoader". ¿Por qué? Esperaba ver contextClassLoader = "java.net.URLClassLoader" (después de todo, cuando un hilo termina su trabajo, se devuelve al grupo de Tomcat y su atributo "contextClassLoader" se establece en cualquier cargador de clases base).
Volcado Heapd 3
Puede ver que no hay un hilo "http-apr-8081-exec-10", pero hay un hilo "http-apr-8081-exec-11" y tiene contextClassLoader = "WebappClassLoader" (¿Por qué no URLClassLoader?) .
Al final tenemos lo siguiente: hay un subproceso "http-apr-8081-exec-11" que tiene la referencia al WebappClassLoader # 1. Y, obviamente, cuando haga "Raíz GC más cercana" en WCL # 1, veré la referencia al hilo 11.
Preguntas
¿Cómo puedo decirle a Tomcat a la fuerza que devuelva el valor antiguo contextClassLoader (URLClassLoader) después de que el hilo termine su trabajo?
¿Cómo puedo asegurarme de que Tomcat no copia el valor antiguo "contextClassLoader" durante la renovación del hilo?
Tal vez, ¿conoces otra forma de resolver mi problema?
Según mi experiencia con este problema, lo que impedía a Tomcat realizar correctamente los cargadores de clases anteriores de GC era que algunos de los marcos de trabajo de ThreadLocal
que estaba utilizando estaban creando (y no se manejaban adecuadamente).
Algo similar a lo que se explica aquí: ThreadLocal y pérdida de memoria
Intenté finalizar correctamente este ThreadLocal
y mi fuga redujo MUCHO. Todavía tenía fugas, pero podía manejar 10 veces más redespliegues que antes.
Definitivamente verificaría los volcados de memoria a objetos que podrían estar conectados de alguna manera a ThreadLocal
s (son muy comunes, especialmente si usas algo para controlar transacciones o cualquier cosa que esté aislada de hilos).
¡Espero que ayude!
Tenemos cientos de instancias de Tomcat que se ejecutan en varios entornos (también de producción) y la única solución razonable que hemos encontrado para este problema es detener y reiniciar cada Tomcat a una hora determinada diariamente (durante la noche).
Hemos probado muchos trucos, pero esta es la solución duradera para nuestros requisitos de tiempo de actividad.
Tomcat no suele ser una buena opción en entornos de producción. Estaba utilizando Tomcat en algunas aplicaciones de producción y descubrí que incluso si el tamaño del montón y otras configuraciones están correctamente configuradas, y cada vez que se vuelve a cargar la aplicación, el consumo de memoria aumenta. Hasta que no reinicie el servicio tomcat, la memoria no se recuperará completamente. Hicimos pruebas en todos los experimentos como, borrando registros, redistribuyendo todas las aplicaciones, reiniciando regularmente Tomcat una vez al mes o una semana durante las horas menos ocupadas. Pero al final tengo que decir que hemos cambiado nuestros entornos de producción a Glassfish y WebSphere.
Tomcat no suele ser una buena opción en entornos de producción. Estaba utilizando Tomcat en algunas aplicaciones de producción y descubrí que incluso si el tamaño del montón y otras configuraciones están correctamente configuradas, y cada vez que se vuelve a cargar la aplicación, el consumo de memoria aumenta. Hasta que no reinicie el servicio tomcat, la memoria no se recuperará completamente. Hicimos pruebas en todos los experimentos como, borrando registros, redistribuyendo todas las aplicaciones, reiniciando regularmente Tomcat una vez al mes o una semana durante las horas menos ocupadas. Pero al final tengo que decir que hemos cambiado nuestros entornos de producción a Glassfish y WebSphere.
Espero que ya hayas pasado por estas páginas:
Pérdida de memoria en una aplicación web Java
https://developers.redhat.com/blog/2014/08/14/find-fix-memory-leaks-java-application/
http://www.tomcatexpert.com/blog/2010/04/06/tomcats-new-memory-leak-prevention-and-detection
Si sus aplicaciones web no están estrechamente relacionadas con Tomcat, entonces puede pensar en usar otro contenedor web. Ahora usamos el Glassfish incluso en máquinas de desarrollo y producción y el día que tomamos esta decisión, ahorramos una gran cantidad de nuestro tiempo. Aunque Glassfish y otros servidores similares toman más tiempo mientras comienzan, ya que no son tan ligeros como el Tomcat, pero después de la vida es un poco más fácil.
Verifique los usos de ThreadLocal que impiden que su ClassLoader sea recolectado como basura. Quite las referencias a sus clases en los valores de ThreadLocal o use https://github.com/codesinthedark/ImprovedThreadLocal lugar de ThreadLocal