26 de febrero de 2010

Buscando memory leaks en Java

Hoy comenzamos :-).

Llevo desde mediados de esta semana buscando alguna herramienta gratuita para poder diagnosticar un "memory leak" (lo voy a traducir por "goteo de memoria") en una aplicacion web Java que hemos desarrollado para un cliente y que se ejecuta en Tomcat.

La aplicacion utiliza JSF e Icefaces y me huele que el error tiene que estar relacionado con este bug de Icefaces: http://jira.icefaces.org/browse/ICE-3027. Ellos dicen que lo tienen arreglado en la 1.7.1, pero a mi me sigue pasando en la 1.7.2 y en la 1.8.

Es fácil de reproducir: te colocas en la pagina inicial, y le das al Ctrl+R como un poseso. Esto inmediatamente hace que suba el tamaño del heap (lo veo con la consola JMX de Java; otro día os cuento como) y se mantiene alto durante mucho tiempo. Luego, si dejamos a Tomcat tranquilo durante un buen rato y le volvemos a hacer otra petición, libera un montón de memoria del heap y se queda como al principio. Formalmente no es un "memory leak", puesto que la memoria se recupera, pero como tarda mucho en hacerlo, es bastante facil que, antes de que a Java le de tiempo a liberar la memoria,  nos de un OutOfMemoryError.

Pero vayamos al grano. Como he dicho, llevo ya tres dias buscando una herramienta gratis para depurar el goteo de memoria y creo que por fin la he encontrado. Se llama Ariadna, y se puede descargar desde este enlace. Es muy sencillito de usar, solo hay que seguir estos pasos:
  • Descargamos la ultima versión y la descomprimimos (en mi caso en el directorio d:\java\ariadna).
  • Desplegamos la aplicación web que queramos probar en Tomcat.
  • Editamos el fichero catalina.bat de Tomcat y añadimos al principio:
set PATH=%PATH%;d:/java/ariadna
set CATALINA_OPTS=%CATALINA_OPTS% -agentlib:ariadna
  • Echamos el fichero ariadna.jar que está en d:\java\ariadna en el directorio WEB-INF\lib de nuestra aplicación web.
  • Ahora creamos una página JSP que colocaremos en el directorio raíz de nuestra aplicación web y que contendrá lo siguiente:
<%@ page contentType="text/html" pageEncoding="UTF-8"%>

<form method="get">
  <input type="submit" name="button" value="Dump heap">
</form>

<%
if( "Dump heap".equals(request.getParameter("button")) ){
 org.mernst.ariadna.agent.Agent.dump();
 out.println( "Heap dumped on "+new java.util.Date() );
}
%>
  • A continuación, apuntamos el navegador a la página ariadna.jsp y le damos al botón. Nos saldra la misma página con el botón y un mensaje que dirá "Heap dumped on ...".
  • ¿Qué hemos conseguido con esto? Pues que ahora en el directorio bin de Tomcat, tengamos dos ficheros con un volcado del contenido del heap. Dichos ficheros son heap y classes.txt. No os lanceis como locos a abrirlos aún ;-).
  • Para ver el contenido de dichos ficheros, Ariadna nos proporciona una herramienta que arranca un servidor Jetty que nos permite visualizar los datos del heap desde un navegador. Para lanzar dicha herramienta solo tenemos que ir al directorio bin de Tomcat y ejecutar el siguiente comando:
java -jar d:/java/ariadna/ariadna.jar

  • Como he dicho, esto arrancará un servidor Jetty, que escucha en el puerto 9999. Nos vamos al navegador, ponemos http://localhost:9999/classes en la barra de direcciones y ¡listos! ya tenemos el listado de clases cargadas, con el número de instancias de cada clase. Si pinchamos en una clase podremos ver todas sus instancias pudiendo, a su vez, pinchar en cada una de ellas para ver desde donde fueron reservadas.
Bueno, pues hasta aquí el artículo de hoy. Ya os contare la semana que viene como nos ha ido la búsqueda del goteo de memoria y si era cosa de Icefaces o nuestra.

Que paséis buen fin de semana.

2 comentarios:

  1. Gracias por el apunte, no conocia la herramienta.

    Para un proyecto utilice el Apache JMeter para crear peticiones sobre hilos y el JProbe para hacer el profiling.

    Ariadna es una herramienta para hacer el profiling no? para ver como y como baja el heap, no?

    gracias por el apunte

    ResponderEliminar
  2. De nada. A ti por tu interes. Tienes el dudoso privilegio de ser la primera persona que comenta algo en mi blog ;-).

    Con Ariadna lo que puedes es hacer un volcado del estado del heap en un momento especifico llamando a la funcion rg.mernst.ariadna.agent.Agent.dump(); desde tu aplicacion. Con esto puedes ver TODOS los objetos que hay reservados en ese momento en la JVM, y como se hacen referencia unos a otros. Con eso y un poco de paciencia puedes diagnosticar memory leaks y/o OutOfMemoryErrors.

    Si lo unico que quieres es ver como sube y baja el heap (paso previo para diagnosticar un memory leak) lo unico que necesitas es la jconsole, que viene "de gratis" con el JDK. En este tutorial un amigo mio explica como usarla:

    http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=MonitorizacionTomcat

    Ahora mismo estoy mirando mas herramientas de profiling de memoria que prometen. Tenia pensado enviar otra entrada al blog cuando las mire mas a fondo, pero por si quieres ir avanzando (que te veo interesado ;-) ) aqui te las pongo:

    HeapAnalyzer de IBM: herramienta grafica que analiza heap dumps de la JVM - http://www.alphaworks.ibm.com/tech/heapanalyzer

    Comando en Mac que analiza un heap dump. Tambien muestra otros comandos utiles y como generar un heap dump al lanzar la JVM - http://developer.apple.com/Mac/library/documentation/Darwin/Reference/ManPages/man1/jhat.1.html

    ResponderEliminar