miércoles, 21 de noviembre de 2007

Recarga de webapplication en Tomcat

Tomcat es sin dudas uno de los motores de servlets mas usados para prototipar aplicaciones Java web generadas con Genexus.
Incluso en la versión Rocha GeneXus busca si está tomact instalado en la maquina de desarrollo y en caso de ser así realiza todas las configuraciones para que se cree en forma automática una webapplication con el nombre de la KB y se copie en forma automática todos los archivos para realizar la ejecución.
Muchas veces doy con personas que me cuentan que luego de estar un rato desarrollando en ese ciclo de implementación, ejecución, modificación y de nuevo ejecución notan que la memoria del tomcat se va a las nubes.
Este post intenta explicar porqué es que ocurre o puede ocurrir ese uso excesivo de memoria.

Una de las características que tiene tomcat es que usa una sola VM de Java para ejecutar todas las webapplication que existan dentro del mismo. Esto hace que cuando se baja, o se intenta recargar una webapplication no sea tan simple como bajar y subir una nueva VM, sino que se crea un nuevo classloader y se intenta liberar la memoria del classloader que antes era el encargado de ejecutar la webapplication. De esta forma si uno como programador no tiene cuidado puede dejar levantada toda la memoria del classloader si hay alguna de las clases que no puede ser liberada. Un caso muy común es programar usando singletons, en ese caso es seguro que cada vez que se baje o se reinicie la webapplication toda la memoria del classloader que maneja esa clase no va a ser liberada.
Como dije anteriormente este se ve acentuado sobre todo cuando se prototipa porque tomcat por defecto hace recarga de la webapplication cuando se mueve una nueva clase, entonces cada nueva clase que se mueve se suma toda la memoria del classloader que no pudo liberar.

Sobre todo para los usuarios GeneXus , que son los que mas leen este blog (eso espero ;)) es bueno saber que en las clases estándar del generador Java teníamos este problema porque hacíamos uso de algunos singletos. Ese comportamiento quedo solucionado a partir del U4 del generador Java de la versión 9.0 de GeneXus.

De todas formas puede pasar que la webapplication sigua sin liberar la memoria del classloader aunque se esta usando el U4 de Java. Generalmente las aplicaciones hacen uso de varias bibliotecas además de las clases estándar del generador… por ejemplo drivers JDBC o clases para el manejo de pdfs, etc. Si algunas de esta bibliotecas no libera sus clases entonces todo el classloader de esa webapp no se libera.
En ese caso la solución es copiar el jar o zip de esa biblioteca el directorio LIB general del tomcat en lugar del directorio LIB propio de la webapplication. Las clases que están en el LIB general del tomcat no se intenta “reciclar” cuando se reinicia una webapplication porque esas clases se cargan en otro classloader que es compartido por todas las webapplications.

Este último tip de copiar las bibliotecas que tienen este problema al LIB general del tomcat puede ser usado también en caso de estar usando las clases estándar de la versión anterior al U4 del generador Java. Eso si, en ese caso todas las webapplication GeneXus que están en el tomcat tienen que estar desarrolladas en la misma versión.

10 comentarios:

Enrique Almeida dijo...

Ignacio:
Gracias por el post. Aclara varias cosas. En estos dias estamos lidiando con problemas de memoria en tomcat, en una aplicacion que usa mucho impresion de PDFs. Con el jconsole/hpjmeter vemos que la memoria sube por escalones, pero no nos es facil encontrar la causa.

La solucion que planteas de tener todas las clases compartidas de genexus en el LIB general del tomcat, no implica que todas las webapps tengan que compartir tambien el archivo de configuracion? (client.cfg)

Hay forma de tener las clases standard de genexus en el LIB general y diferentes client.cfg? Lo he intentado, pero no lo logre.

LO que todas las aplicaciones GeneXus de un tomcat tengan que estar en la misma version, lo veo como un mal menor, frente al problema de compartir el client.cfg
Gracias!
Enrique

Ignacio Roqueta dijo...

Si, en caso de poner las clases estandar en el LIB general del tomcat entonces todas las webapplication GeneXus tienen que usar el mismo archivo de configuracion client.cfg.
Eso como tu dices es una limitacion importante, de todas formas Yo creo que tener en produccion mas de una webapplication en el Tomcat puede resultar perfjudicial porque todas comparten los recursos de la misma VM.
Ojo, es un comentario muy personal, no quiero que se me enojen los defensores del tomcat.

Israel Cors dijo...

Buen dia Ignacio!
Este es un tema bastante particular para quienes usamos ese motor tanto para prototipar como para nuestras webapps en producción. En nuestro grupo de desarrollo nos hemos encontrado muchas veces con este famoso "Out of memory". Muchas veces le asignabamos el error al tomcat, "que su versión", "que está en guindous", "que la gestión de memoria",... que bla bla!...
Cuando nos pusimos exquisitos y queríamos "ver la pelusa" del problema nos encontramos con varios docs que anunciabamos lo que temíamos: la mayoría de las veces tomcat era la víctima de los errores de programación nuestros en nuestras webapps. Asi y todo mucho no podíamos hacer mucho porque aplicaciones generadas con gx no podían ser retocadas en su código para agregarle cierto control de disparo del System.gc() o evitar de alguna manera estos singletons que mencionás y que crean "hard references" en tiempo de ejecución.
Como podíamos evitar todo estos problemas?
Mas allá de poder encontrar alguna manera de parametrizar, "afinar", "tunear" al tomcat, (seteando el PermGSize por ejemplo), tuvimos que estudiar como trabajar adecuadamente en nuestro ambiente de desarrollo. Por ejemplo si el proyecto era compartido, configuramos un apache con tomcat (ambos tunneados) para que aguantara las recargas. Si el proyecto es particular (un solo programador) que utilizara un tomcat local en la maquina, para que no saturara al tomcat de test (en el servidor de test). Porsupuesto, eventualmente, el reinicio del servicio web en el servidor test es inevitable.
Para el servidor de aplicaciones, aprendimos que las actualizaciones (pasajes a prod) en el servidor de Producción se hagan en momento no picos de uso del servicio, fuera de horario, o en horarios donde encontramos que el uso del servicio es mínimo.
De esta manera nos encontramos con un ambiente un tanto mas estable. Al día de hoy, al servicio deno hemos tenido que reiniciarlo nunca.
Obviamente, trabajamos con proyectos generados con distintas versiones de gx, drama que se hace presente en este contexto.

Saludos cordiales.
Israel Cors
Instituto de Seguridad Social


Una doc de referencia:
http://wiki.apache.org/tomcat/OutOfMemory

Federico Wagner dijo...

Con Tomcat existe la posibilidad de tener más de una instancia, con lo cual podemos tener las aplicaciones aisladas una de otra. Esto puede ser util en el caso que necesitemos tener aplicaciones anteriores a gx9.0 u4, en una instancia ponemos todo lo generado ocn U4 y las otras en otra.

Guzmán dijo...

Ignacio, Enrique: Muy bueno los aportes. Tengo unas preguntas. Cuando dicen "poner las clases estandar en el LIB general del tomcat". Estas classes son por ejemplo: GXUtils.jar, driver de mysql, etc. No?

Cuando dicen "todas las webapplication GeneXus tienen que usar el mismo archivo de configuracion client.cfg" donde es que está ubicado este Client.cfg?
Cada WebApp tiene su Client.cfg, hay algún otro tipo de Client.cfg que pueda ser compartido por mas de una webapp?

En mi caso estoy tratando de resolver el problema de cuando el tomcat crece en la memoria y no la va liberando.

Gracias,
Guzmán

Anónimo dijo...

Buenos dias: Estoy teniendo el mismo problema al tratar de general pdf. Estoy utilizando gx 9 y u4. El tomcat incrementa el uso de memoria y nunca la libera. Probé con el tip que publicaron. Pero tampoco mejoro. Alguien puede darme una mano? Muchas gracias

Jose Rodriguez dijo...

Hola a todos... soy nuevo con esto del genexus y tomcat, tengo una aplic que corre bien en una maquina windows y quiero ponerla en un servidor linux con lo mismo tomcat 5 y java 1.5 pero no corre, algunos formularios no se muestran no imprime reporte etc.. en pocas palabras quiero migrar de windows a linux y me da problemas.... que debo hacer para cambiar de servidor? Saludos

Jose Rodriguez dijo...

Hola a todos... soy nuevo con esto del genexus y tomcat, tengo una aplic que corre bien en una maquina windows y quiero ponerla en un servidor linux con lo mismo tomcat 5 y java 1.5 pero no corre, algunos formularios no se muestran no imprime reporte etc.. en pocas palabras quiero migrar de windows a linux y me da problemas.... que debo hacer para cambiar de servidor? Saludos

Jose Rodriguez dijo...

Hola a todos... soy nuevo con esto del genexus y tomcat, tengo una aplic que corre bien en una maquina windows y quiero ponerla en un servidor linux con lo mismo tomcat 5 y java 1.5 pero no corre, algunos formularios no se muestran no imprime reporte etc.. en pocas palabras quiero migrar de windows a linux y me da problemas.... que debo hacer para cambiar de servidor? Saludos

Jose Rodriguez dijo...

Hola a todos... soy nuevo con esto del genexus y tomcat, tengo una aplic que corre bien en una maquina windows y quiero ponerla en un servidor linux con lo mismo tomcat 5 y java 1.5 pero no corre, algunos formularios no se muestran no imprime reporte etc.. en pocas palabras quiero migrar de windows a linux y me da problemas.... que debo hacer para cambiar de servidor? Saludos