viernes, 15 de febrero de 2013

Analizando Leaks

Hace unos años publiqué un artículo donde contaba como había encontrado leaks de una aplicación Genexus Java. Ahora me enfrento otro problema levemente distinto, nuevamente en Tomcat tengo una aplicación que aparentemente no tiene leaks, pero cuando actualizamos la versión de la aplicación (redeploy) la versión anterior queda colgada.

Esto apunta a otro tipo de leak, en el que se crea una referencia entre una clase de sistema (de las cuales es propietario el Tomcat) y un objeto de mi aplicación, con lo cual la memoria utilizada por la versión vieja de la aplicación no puede ser reciclada y a la larga se produce un error del tipo:
java.lang.OutOfMemoryError: PermGen space
  Los pasos que utilicé para detectar el problema son:
  1. Crear un dump y levantarlo en jhat explica mi post anterior.
  2. Buscar en jhat las instancias de org.apache.catalina.loader.WebappClassLoader.
  3. Abrir las instancias una por una y quedarme solo con las que tienen la ruta de mi aplicación
  4. Las instancias que no están referenciadas por la clase org.apache.catalina.loader.WebappLoader son leaks.
  5. Elegir una de las instancias para estudiarla y copiar el Id del objeto.
  6. En el menú de jhat abrir Execute Object Query Language (OQL) query y ejecutar:
    select heap.livepaths(o) from org.apache.catalina.loader.WebappClassLoader o where objectid(o) == "XXX"
    Sustituyendo XXX por el id que se quiere estudiar (ej: 0xa3ed2510).
  7. Copiar el resultado en un editor de texto
  8. Reemplazar ", " por ",\r\n" para obtener una línea por referencia
  9. Buscar referencias problemáticas:
    • Java Local Reference
    • System Class Reference
En mi caso encontré clases referenciadas por java.lang.ThreadLocal, que es uno de los casos clásicos de Leaks en Java.

El problema en particular estaba en las clases del JDBC de Oracle: oracle.sql.AnyDataFactory y oracle.jdbc.driver.OracleDiagnosabilityMBean. Cómo no se que posible arreglo tendrá, decidí mover el driver al lib global de Tomcat y de esa forma puedo hacer redeploy de la aplicación sin que el driver deje la versión vieja colgada.

Nota:
Estoy usando Tomcat 6.0.24, en versiones anteriores de Tomcat no existía la detección de Leaks, a partir de esta versión se detectan automáticamente algunos leaks y dicha información queda en el log de Tomcat. En esta versión también se resuelve de forma automática el caso típico de leak debido a que no se desregistra el driver JDBC, pero otros leaks (como los que me enfrento ahora) no se resuelven automáticamente y Tomcat no es capaz de determinar con certeza cuales son las situaciones que efectivamente se convierten en un leak.

No hay comentarios: