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.