miércoles, 13 de mayo de 2009

Memory leaks en Java

Hace poco tuve un inconveniente desarrollando una aplicación web con Genexus/Java, la memoria que consumía el tomcat crecía hasta que daba un error java.lang.OutOfMemoryError: Java heap space.

El problema se daba durante la ejecución de un proceso batch muy largo y era claro que una vez que terminaba no liberaba la memoria que había utilizado, entonces ¿Como identificar que es lo que está quedando colgado?

Lo primero que usé fue JConsole, que una vez habilité JMX en el tomcat, me permitió ver el consumo de memoria global, como crecía y decrecía, pero seguía sin ver que objetos eran los que estaban quedando colgados.

Busqué varios profilers para Java y encontré que principalmente apuntan a mostrar los tiempos de ejecución de las operaciones, así que en este caso no me servían y me di cuenta que tenía que buscar alguna herramienta para analizar la memoria. Estoy seguro de que existen muchas y hay mejores soluciones que la que utilicé, pero lo que encontré sirvió a mis propósitos y tiene la ventaja y contra de no tener que estar analizando los datos en tiempo real.

Solución: JConsole + jhat (Java Heap Analysis Tool), ambas son herramientas que vienen con el JDK de SUN, simplemente hay que buscarlos en la carpeta bin.

jhat es un programa que dado un "memory dump" levanta un servidor web que permite navegar la memoria en dicho dump, además de presentar útiles histogramas de instancias y cantidad de memoria por clase. Pero hay que tener en cuenta que para un dump grande 100MB+ hay que pasarle parámetros para agrandar el heap del propio jhat, sino se queda sin memoria y hay que sentarse a esperar porque se toma su tiempo para procesar los datos.

Entonces los pasos a seguir son:
  1. Habilitar JMX en el Tomcat, agregar a la configuración los siguientes parámetros:
    -Dcom.sun.management.jmxremote
    -Dcom.sun.management.jmxremote.port=”9004″
    -Dcom.sun.management.jmxremote.authenticate=”false”
    -Dcom.sun.management.jmxremote.ssl=”false”
  2. Ejecutar los procesos que se sabe que dejan memoria colgada o usar la aplicación hasta que la memoria crezca considerablemente.
  3. Iniciar JConsole y conectarse al servidor en el puerto 9004.
  4. En el tab MBeans ejecutar com.sun.management.HotSoptDiagnostic.dump usando como parámetro "C:\dump.bin" (o la ruta que venga bien).
  5. Ejecutar jhat pasando como parámetro el archivo a procesar, opcionalmente se le puede agrandar la memoria de heap porque con archivos grandes puede caer.
    jhat -J-Xmx400m dump.bin
  6. Entrar a la url http://localhost:7000/ donde debería aparecer la página de bienvenida de jhat, con un resumen de las clases que hay en memoria y al final unos links a consultas preparadas. La que me resultó más útil es el histograma.
Una vez que uno entra al histograma presentado por jhat, seguramente va a encontrar algún tipo básico con mayor cantidad de instancias, por ejemplo int o char[], es lógico ya que cualquier objeto está compuesto por tipos básicos, pero inmediatamente abajo se pueden encontrar otras clases con cantidad un poco menor de instancias y probablemente éstas sean las que nos interesan.

Ahora llegamos al punto en el que identificamos cuales son los objetos que están ocupando más memoria, al hacer click en la clase que nos interesa analizar, podemos ver algunos datos interesantes de la misma y debajo una lista de todas las instancias de dicha clase, así que elegimos una instancia cualquiera y podemos empezar a navegar a través de sus referencias, para encontrar el objeto raíz que está dejando las instancias colgadas.

Una vez identificado el objeto, estamos listos para corregir el error, o como en mi caso quejarnos a soporte y tratar de encontrar un workaround!

jueves, 11 de diciembre de 2008

Streaming en Genexus

Bueno, en serio, prometo escribir más seguido. Hace unos meses un colega me dijo que tenía que empezar a escribir, que es sencillo, todos los días uno siempre resuelve algún problema y bueno escribir sobre eso.

Ahora escribo sobre algo que me pareció importante hace unos meses, no se si el término Streaming aplica totalmente, pero pienso que se acerca bastante.

Cuando usamos Genexus para realizar reportes en web, lo más común es realizar los reportes en PDF, ya que de está forma el usuario puede verlo y después decidir que hacer con él, guardarlo, imprimirlo o descartarlo.

Ahora, las aplicaciones que hacemos no son de juguete, son de negocio y a veces tenemos reportes de algunos cientos de páginas.

Por suerte Genexus nos ofrece la opción de generar los reportes directo por el protocolo HTTP y uno piensa, ¡que suerte! no tengo que guardar un archivo grandecito en el servidor, para que después el usuario lo baje, lo cual generaría un overhead bastante grande en el servidor.

Lamentablemente, si vien Genexus nos ofrece la opción de generar los reportes por HTTP, no hace streamming. ¿Que quiero decir con esto? Bueno, Genexus calcula todo el reporte en memoria y luego lo devuelve de un saque, lo cual puede ser aún peor para la escalabilidad del servidor. ¡Por favor corriganmé si me equivoco, pero busqué por todos lados y aparentemente este siempre es el comportamiento!

Lamentablemente esto no se limita sólo a los reportes PDF, supongamos que queremos exponer un procedimiento que devuelve un XML "gigante" para hacer interfaz con otro sistema. Bueno lógicamente empezamos por abrir un stream XML en el Response HTTP y luego vamos escribiendo ese XML, en este caso sería muy lógico que el XML se fuera mandando al cliente a medida que se va generando, pero nuevamente Genexus genera todo el XML en memoria y luego lo devuelve.

De repente lo ideal no es que el comportamiento siempre sea hacer streaming, ya que una de las principales virtudes de Genexus es ocultar tecnicismos al programador. Y por ejemplo cuando se trabaja con streaming hay que tener cuidado de escribir los cabezales antes de empezar a hacer el streaming. Pero si estaría bueno tener una opción de streaming (debidamente documentada con esos tecnicismos).

sábado, 1 de marzo de 2008

Nuevo Celular

Bueno, pasó un año, ya era hora de agregar algo más.

Hace una semana firmé con ancel, un contrato con el cual me regalaron un LG KU250, es un celular 3G barato, en realidad el más barato del mercado y ganador del concurso 3G for all.


En realidad los planes de datos 3G todavía son muy caros, así que por ahora solo lo voy a usar como un celular GSM común y corriente, excepto que hasta hoy me dejaban hacer videollamadas gratis.

Otra funcionalidad que me gusta, es que soporta bluetooth, lo malo es que me costó mucho encontrar la aplicación para sincronizar con la PC (el sitio de LG deja mucho que desear), así que aprovecho a pasarles un link por si a alguien le hace falta: http://www.smart.com.ph/Buddy/promos/LG_KU250.htm

Por ahora la peor contra es la duración de batería, a los que conozco que tienen el mismo celular les ha pasado que les dura muy poco. Pero todos lo tienen hace poco tiempo, capaz que se debe simplemente al uso intensivo o a que son las primeras cargas.

Bueno esto es todo amigos, capaz que les escribo más seguido.

viernes, 2 de febrero de 2007

Hoy arranco

Este es mi primer blog, y lo hice solo por curiosidad. Así que hoy digo "Hoy arranco", pero mañana capaz que lo dejo. El tiempo dirá.