Depurando desde volcados

Diagnostique más que fugas de memoria con Memory Analyzer

Memory Analyzer es una poderosa herramienta para diagnosticar fugas de memoria y problemas de huella desde el volcado de un proceso de Java™ . También puede darle información detallada sobre su código de Java y permitirle depurar algunos problemas complicados desde sólo un volcado, sin la necesidad de insertar código de diagnóstico. En este artículo, aprenderá cómo generar volcados y usarlos para examinar el estado de su aplicación.

Chris Bailey, Java Support Architect, Software IBM

Chris BaileyChris Bailey es un miembro del equipo de IBM Java Technology Center en el Laboratorio de Desarrollo de Hursley Park en el Reino Unido. Como el arquitecto técnico para la organización de servicio y soporte de IBM Java, es responsable de habilitar a los usuarios del SKD de IBM para Java para que entreguen despliegues de aplicación exitosos. Chris también está involucrado en la recolección y evaluación de nuevos requisitos, la entrega de nuevas posibilidades y herramientas de depuración, en hacer mejoras en documentación y en mejorar la calidad del SDK de IBM para Java.


Nivel de autor profesional en developerWorks

Kevin Grigorenko, Software Engineer, WebSphere Application Server SWAT Team, Software IBM

Author photoKevin Grigorenko es un ingeniero de software en el equipo SWAT de WebSphere Application Server, que proporciona soporte en el sitio y remoto de defectos de productos suplementarios en todo el mundo. Se enfoca en la determinación de problemas para WebSphere Application Server y productos de pila relacionados y sistemas operativos, incluyendo las JVMs de IBM y Oracle, AIX, Linux, Solaris, Windows, z/OS, HP/UX, e i5/OS.



Andrew Johnson, Java Tools Developer and Eclipse Memory Analyzer Tool Committer, Software IBM

Andrew JohnsonAndrew Johnson es un Ingeniero Colegiado y un Ingeniero de Software de Asesoramiento en IBM Java Technology Center en Hursley, Inglaterra. Se unió a IBM en 1988, después de recibir un B.A. en Ingeniería Electrónica de la Universidad de Cambridge. Desde 1996 ha trabajado en máquinas virtuales de Java, compiladores just-in-time y herramientas para diagnosticar problemas de Java. Escribió un adaptador para Eclipse Memory Analyzer para leer volcados de VMs de IBM y ahora es un committer en ese proyecto.



16-05-2012

Variantes de Memory Analyzer

IBM Monitoring and Diagnostic Tools for Java - Memory Analyzer ofrece las posibilidades de diagnóstico de Eclipse Memory Analyzer Tool (MAT) para las Máquinas Virtuales de IBM para Java al extender la versión 1.0 de Eclipse MAT usando IBM Diagnostic Tool Framework for Java (DTFJ). DTFJ habilita el análisis de almacenamiento dinámico de Java usando volcados a nivel de sistema operativo e IBM Portable Heap Dumps. La variante de IBM está disponible como parte de IBM Support Assistant (ISA). Hay un plug-in de DTFJ disponible para Eclipse MAT autónomo. Vea Recursos para obtener enlaces de descarga.

Añadir sentencias de depuración a su código para grabar los campos en un objeto, o incluso colecciones de datos completas, es un enfoque común de resolución de problemas. Frecuentemente debe hacer esto en forma iterativa a medida que descubre que necesita más y más información para entender y solucionar el problema. Aunque este proceso puede ser efectivo, algunas veces puede fallar para dar sus frutos: el volumen de código de depuración puede causar el problema para desaparecer, podría necesitar añadir depuración a código que no le pertenece, la depuración puede requerir que reinicie procesos, o el impacto de rendimiento general de la depuración podría evitar que la aplicación se ejecute.

Memory Analyzer es una herramienta para todas las plataformas y de código abierto que puede usar no sólo para diagnosticar problemas de memoria, sino también para obtener un enorme conocimiento del estado y comportamiento de toda una aplicación de Java. Al leer en una instantánea el volcado creado por el tiempo de ejecución de Java mientras la aplicación se está ejecutando, Memory Analyzer le da una forma de diagnosticar problemas complicados que el código de depuración puede fallar para exponerlos.

Este artículo le muestra cómo generar volcados y usarlos para examinar y diagnosticar el estado de su aplicación. Con Memory Analyzer, puede inspeccionar hebras, objetos, paquetes y colecciones completas de datos para depurar problemas de código de Java que van más allá de las fugas de memoria.

Tipos de volcado de instantánea

Memory Analyzer actualmente puede trabajar con tres tipos de volcado:

  • IBM Portable Heap Dump (PHD): Este formato propiedad de IBM contiene sólo el tipo y tamaño de cada objeto de Java en el proceso y las relaciones entre los objetos. Este formato de archivo de volcado es significativamente más pequeño que otros formatos y contiene la mínima información. Sin embargo, los datos son normalmente suficientes para diagnosticar fugas de memoria y obtener un entendimiento básico de la arquitectura y huella de la aplicación.
  • Volcado binario de HPROF: El formato binario de HPROF contiene todos los datos presentes en el formato de IBM PHD así como los datos primitivos alojados dentro de los objetos de Java, y los detalles de hebra. Puede observar los valores alojados en los campos dentro de los objetos y ver qué métodos se estaban ejecutando al momento en que fue tomado el volcado. Los datos primitivos adicionales hacen que los volcados de HPROF sean significativamente más grandes que los volcados de formato PHD; son aproximadamente del mismo tamaño que el almacenamiento dinámico de Java utilizado.
  • Volcados de sistema de IBM: Cuando el tiempo de ejecución de IBM Java está siendo usado, el archivo de volcado del sistema operativo nativo — un archivo principal en AIX® o Linux, un minivolcado en Windows® o un volcado de SVC en z/OS®— puede ser cargado en Memory Analyzer. Estos volcados contienen toda la imagen de memoria de la aplicación en ejecución — toda la información y datos en el formato de HPROF, así como toda la memoria nativa y la información de hebra. Este es el formato de archivo de volcado más grande y completo.

Ambos tipos de volcado de IBM están disponibles sólo con el plug-in de Diagnostic Tool Framework for Java (DTFJ) instalado (vea Recursos y la barra lateral de variantes de Memory Analyzer ).

La Tabla 1 resume las diferencias entre los tipos de archivo de volcado:

Tabla 1. Resumen de las características de los tipos de volcado
Formato de volcadoTamaño aproximado en discoObjetos, clases y cargadores de clasesDetalles de hebraNombres de campoReferencias de matriz y campoCampos primitivosContenido primitivo de matrizRaíces precisas de recolección de basuraMemoria nativa y hebras
IBM PHD20 por ciento del tamaño de almacenamiento dinámico de JavaYCon With Javacore*NYNNNN
HPROFTamaño de almacenamiento dinámico de JavaYYYYYYYN
Volcados de sistema de IBMTamaño de almacenamiento dinámico de Java + el 30 por cientoYYYYYYYY

*Al cargar en un archivo javcore.txt (archivo de volcado de hebra de IBM) y un archivo heapdump.phd que fueron generados al mismo tiempo, Memory Analyzer vuelve disponibles los detalles de hebra en el volcado de formato de IBM PHD.

Los formatos de volcado de HPROF y de sistema de IBM pueden ser bien comprimidos, normalmente alrededor de un 20 por ciento de su tamaño original, usando herramientas del sistema operativo.


Obteniendo volcados de instantánea

Hay distintos mecanismos disponibles para obtener los diversos volcados para cada uno de los tiempos de ejecución de Java, proporcionando flexibilidad que le permite generar volcados de instantánea para escenarios que van más allá de aquellos que involucran OutOfMemoryErrors. Los mecanismos disponibles dependen del proveedor de tiempo de ejecución de Java que esté usando.

Requisitos Previos

Para todos los tipos de volcado, debe asegurar suficiente espacio en disco para los volcados, de forma que no sean truncados. La ubicación predeterminada de los volcados es el directorio en funcionamiento actual del proceso de la JVM. Para JVMs de IBM, puede cambiar esto con la opción de línea de comando del archivo -Xdump . Para la JVM de HotSpot, puede cambiarlo usando la opción de línea de comando -XX:HeapDumpPath . Vea Recursos para obtener enlaces hacia la sintaxis relevante.

Los volcados del sistema operativo pueden ser usados para JVMs de IBM y de HotSpot. Para la JVM de IBM, puede crear volcados con la herramienta jextract (enviada con el JDK) y cargarlos directamente en Memory Analyzer; para la JVM de HotSpot, usted usa la herramienta jmap para extraer volcados de almacenamiento dinámico de volcados principales. (Discutiremos ambas técnicas con más detalle más adelante en este artículo.) Sin embargo, en algunos sistemas operativos, debe asegurarse de que el proceso se esté ejecutando con suficientes ulimits antes de crear el volcado de principal; de otra manera, el volcado principal se truncará y el análisis será limitado. Si los ulimits son incorrectos, debe modificarlos y reiniciar el proceso antes de recolectar un volcado. Vea Recursos para obtener enlaces hacia información detallada sobre la obtención de volcados de sistema desde AIX, Linux®, z/OS y Solaris.

Obteniendo un volcado de instantánea: tiempos de ejecución de HotSpot

Los tiempos de ejecución de Java basados en HotSpot generan sólo el volcado de formato de HPROF. Puede elegir entre varios métodos interactivos y un método basado en eventos para generar el volcado:

  • Métodos interactivos:
    • Usando un Ctrl+Break: Si la opción de línea de comando -XX:+HeapDumpOnCtrlBreak está establecida para la aplicación en ejecución, un volcado de formato de HPROF es generado junto con un volcado de hebra cuando un evento de Ctrl+Break, o SIGQUIT (normalmente generado usando kill -3), es enviado mediante la consola. Esta opción puede no estar disponible en algunas versiones, en cuyo caso intente:

      -Xrunhprof:format=b,file=heapdump.hprof
    • Usando la herramienta jmap : La herramienta de utilidad jmap (vea Recursos), entregada en el directorio bin del JDK, proporciona una opción para solicitar un volcado de HPROF desde el proceso en ejecución. Con Java 5, use:

      jmap -dump:format=b pid

      Con Java 6, use esta versión, donde live es opcional y resulta sólo en los objetos "en vivo" que se están escribiendo en el ID de proceso (PID) del archivo de volcado:

      jmap -dump[live,]format=b,file=pid de nombre de archivo
    • Usando el sistema operativo: Use el comando gcore no destructivo o los comandos kill -6 o kill -11 destructivos para producir un archivo principal. Después, extraiga un volcado de almacenamiento dinámico desde el archivo principal usando jmap:

      jmap -dump:format=b,file=heap.hprof ruta hacia el núcleo ejecutable de java
    • Usando la herramienta JConsole: Una operación dumpHeap es proporcionada bajo el MBean HotSpotDiagnostic en JConsole. Esta operación solicita que un volcado de HPROF sea generado.
  • Método basado en eventos:
    • En un OutOfMemoryError: Si la opción de línea de comando -XX:+HeapDumpOnOutOfMemoryError está establecida para la aplicación en ejecución, un volcado de formato de HPROF es generado cuando un OutOfMemoryError ocurre. Es ideal tener esto en su lugar para sistemas de producción, porque casi siempre es requerido para diagnosticar problemas de memoria y no genera una sobrecarga de rendimiento constante. En releases anteriores de tiempos de ejecución de Java basados en HotSpot, no hay límite para los volcados de almacenamiento dinámico que son producidos en este evento por ejecución de JVM; en releases más recientes, un máximo de un volcado de almacenamiento dinámico es producido en este evento por ejecución de JVM.

Obteniendo un volcado de instantánea: tiempos de ejecución de IBM

Los tiempos de ejecución de IBM proporcionan motores de rastreo y volcado que pueden generar volcados de formato de PHD o de sistema en un gran número de escenarios interactivos y basados en eventos. También puede generar volcados interactivos usando la herramienta Health Center o programáticamente usando la API de Java.

  • Métodos interactivos
    • Usando un SIGQUIT o Ctrl+Break: Cuando un Ctrl+Break o SIGQUIT (normalmente generado usando kill -3) es enviado al tiempo de ejecución de IBM, un evento de usuario es generado en el motor de volcado de IBM. De forma predeterminada, este evento sólo genera un archivo de volcado de hebra (javacore.txt). Puede usar la opción -Xdump:heap:events=user para generar un volcado de formato de PHD, o la opción -Xdump:system:events=user para generar un volcado de sistema de la aplicación de Java.

    • Usando el sistema operativo para producir un volcado de sistema:

      • AIX: gencore (o los destructivos kill -6 o kill -11)
      • Linux/Solaris: gcore (o los destructivos kill -6 o kill -11)
      • Windows: userdump.exe
      • z/OS: SVCDUMP o volcado de consola
    • Usando IBM Monitoring and Diagnostics Tools for Java - Health Center: La herramienta Health Center proporciona una opción de menú para solicitar un volcado de PHD o de sistema desde un proceso de Java en ejecución (vea Recursos).

  • Métodos basados en eventos. Los motores de rastreo y volcado de IBM proporcionan un conjunto flexible de posibilidades para generar volcados de PHD y de sistema en un gran número de eventos, desde excepciones siendo lanzadas hasta métodos siendo ejecutados. Usándolos, debe poder generar volcados para la mayoría de los escenarios de problema que desea diagnosticar:
    • Usando el motor de volcado de IBM: El motor de volcado proporciona un gran número de eventos en los cuales puede producir un volcado de PHD o de sistema. Más aún, le permite filtrar en tipos de esos eventos para ejercer un control de grano fino sobre cuándo generar volcados.

      Puede ver los eventos predeterminados al usar la opción -Xdump:what . Notará, por ejemplo, que se producen un heapdump.phd y un javacore.txt en las primeras cuatro excepciones de OutOfMemoryError en la JVM.

      Para recolectar más datos, puede producir un volcado de sistema en lugar de un volcado de almacenamiento dinámico en una excepción OutOfMemoryError :

      -Xdump:heap:none -Xdump:java+system:events=systhrow,
       filter=java/lang/OutOfMemoryError,range=1..4,request=exclusive+compact+prepwalk

      Algunas excepciones, por ejemplo NullPointerExceptions, son generadas comúnmente en la mayoría de las aplicaciones por una amplia gama de código. Esto hace que sea difícil generar un volcado en un NullPointerException particular de interés. Para ayudarle a ser más específico sobre en qué excepción generar el volcado, un nivel extra de filtrado es proporcionado para "lanzar" y "atrapar" eventos que le permitan especificar los métodos de lanzado y atrapado, respectivamente. Usted hace esto al añadir un separador # y después añadir el método de lanzado o atrapado según sea apropiado. Por ejemplo, esta opción produce un volcado de sistema cuando una NullPointerException es lanzada por el método bad() :

      -Xdump:system:events=throw,
             filter=java/lang/NullPointerException#com/ibm/example/Example.bad

      Esta opción produce un volcado de sistema cuando una NullPointerException es atrapada por el método catch() :

      -Xdump:system:events=catch,
             filter=java/lang/NullPointerException#com/ibm/example/Example.catch

      Además de filtrar en los eventos, también puede especificar una gama de eventos en los cuales desea que se generen volcados. Por ejemplo, esta opción produce un volcado sólo en la quinta ocurrencia de una NullPointerException:

      -Xdump:system:events=throw, filter=java/lang/NullPointerException,range=5

      Esta opción usa un intervalo para producir un volcado sólo en la segunda, tercera y cuarta ocurrencia de una NullPointerException:

      -Xdump:system:events=throw, filter=java/lang/NullPointerException,range=2..4

      La Tabla 2 resume los eventos y filtros más útiles:


      Tabla 2. Eventos de volcado disponibles
      EventoDescripciónFiltrado disponibleEjemplo
      gpfFalla de protección general (crash)-Xdump:system:events=gpf
      userSeñal generada por el usuario (SIGQUIT o Ctrl+Break)-Xdump:system:events=user
      vmstopConclusión de la VM, incluyendo llamada al código de salida System.exit()-Xdump:system:events=vmstop,filter=#0..#10
      Generar un volcado de sistema en la conclusión de la VM con un código de salida entre 0 y 10.
      loadCarga de claseNombre de clase-Xdump:system:events=load,filter=com/ibm/example/Example
      Generar un volcado de sistema cuando la clase com.ibm.example.Example es cargada.
      unloadDescarga de claseNombre de clase-Xdump:system:events=unload,filter=com/ibm/example/Example
      Generar un volcado de sistema cuando la clase com.ibm.example.Example es descargada.
      throwUna excepción siendo lanzadaNombre de clase de excepción-Xdump:system:events=throw,filter=java/net/ConnectException
      Genera un volcado de sistema cuando una ConnectException es generada.
      catchUna excepción siendo atrapadaNombre de clase de excepción-Xdump:system:events=catch,filter=java/net/ConnectException
      Genera un volcado de sistema cuando una ConnectException es atrapada.
      systhrowUna excepción de Java está a punto de ser lanzada por la JVM. (Esta es distinta del evento throw porque sólo es desencadenada por condiciones de error detectadas internamente en la JVM.)Nombre de clase de excepción-Xdump:system:events=systhrow,filter=java/lang/OutOfMemoryError
      Generar un volcado de sistema cuando un OutOfMemoryError es generada.
      allocationUn objeto de Java es asignadoTamaño del objeto siendo asignado-Xdump:system:events=allocate,filter=#5m
      Genera un volcado de sistema cuando un objeto más grande de 5MB es asignado.
    • Usando el motor de rastreo de IBM: El motor de rastreo permite que los volcados de PHD y de sistema sean desencadenados en entrada o salida de método para cualquier método de Java ejecutándose en la aplicación. Usted consigue esto al usar la palabra clave trigger para las opciones de línea de comandos -Xtrace que controlan el motor de rastreo de IBM. La sintaxis para la opción de desencadenamiento es:
      method{methods[,entryAction[,exitAction[,delayCount[,matchcount]]]]}

      Añadir la siguiente opción de línea de comando a la aplicación produce un volcado de sistema cuando el método Example.trigger() es llamado:

      -Xtrace:maximal=mt,trigger=method{com/ibm/example/Example.trigger,sysdump}

      Esta opción de línea de comando produce un volcado de PHD cuando el método Example.trigger() es llamado:

      -Xtrace:maximal=mt,trigger=method{com/ibm/example/Example.trigger,heapdump}

      Sin embargo, se recomienda que establezca un intervalo de forma que no cree volcados cada vez que el método es llamado. Este ejemplo ignora las primeras cinco llamadas a Example.trigger() y después desencadena un volcado:

      -Xtrace:maximal=mt,trigger=method{com/ibm/example/Example.trigger,sysdump,,5,1}

      Note que un término vacío es usado para exitAction en este ejemplo, ya que estamos desencadenando los volcados sólo en la entrada de método.
  • Métodos programáticos: Los tiempos de ejecución de IBM también proporcionan una clase com.ibm.jvm.Dump con los métodos javaDump(), heapDump() y systemDump() . Estos generan volcados de hebra, volcados de PHD y volcados de sistema, respectivamente.

Adquiriendo un volcado usando Memory Analyzer

Así como los métodos para obtener volcados que son proporcionados por los tiempos de ejecución mismos, Memory Analyzer también proporciona una opción Acquire Heap Dump, mostrada en la Figura 1, que le permite desencadenar y cargar un volcado de instantánea desde un proceso de Java ejecutándose en la misma máquina que Memory Analyzer:

Figura 1. Usando la función Acquire Heap Dump en Memory Analyzer
Usando la función Acquire Heap Dump en Memory Analyzer

En tiempos de ejecución basados en HotSpot, Memory Analyzer genera el volcado usando jmap. Para los tiempos de ejecución de IBM, el volcado es generado usando la funcionalidad "late attach" de Java y la API programática. Java 6 SR6 es requerido para que opere la función, ya que los releases anteriores no contienen la función "late attach".

Requisitos de post-procesamiento

Para volcados de sistema de IBM, el volcado debe ser post-procesado usando la herramienta jextract enviada con el JDK:

jextract core

Idealmente, jextract es ejecutado en la misma máquina virtual que produjo el volcado, usando jextract desde la misma instalación del JDK que produjo el volcado y con acceso de lectura para las mismas bibliotecas con las que el proceso de java se estaba ejecutando. Dado que jextract puede consumir muchos ciclos de CPU procesando el volcado, esto puede ser inaceptable en algunos sistemas de producción. En este caso, el volcado debe ser procesado en el sistema más coincidente, como un sistema de prueba de pre-producción. Las versiones de Renovación de Servicio (SR) y de Fixpack (FP) de los tiempos de ejecución de Java deben coincidir.

jextract produce un archivo ZIP que incluye el volcado principal original, una representación procesada del volcado, el ejecutable de Java y las bibliotecas utilizada por el proceso de java . Puede suprimir el volcado principal original (no comprimido) después de ejecutar jextract. El archivo ZIP es lo que debe cargar en Memory Analyzer.

Puede extraer un volcado de PHD desde un volcado de sistema en el que se ejecutó jextract al cargar el ZIP en jdmpview y ejecutar el comando heapdump (vea Recursos).


Usando Memory Analyzer para analizar problemas

Memory Analyzer puede diagnosticar OutOfMemoryErrors al observar las áreas de la aplicación que tienen fugas de memoria o que tienen un requisito de huella que es demasiado grande para la memoria disponible. Memory Analyzer hace una detección de fugas automática y genera un informe de Sospechosos de Fuga (vea Recursos).

Los datos adicionales que están disponibles en los volcados de sistema de HPROF y de IBM, particularmente los nombres de campo y los valores de campo — junto con las posibilidades de la vista Inspector y Object Query Language (OQL) — también hacen posible diagnosticar una gama más amplia de tipos de problema que simplemente "¿Qué está usando toda la memoria?". Por ejemplo, puede determinar el factor de ocupación y carga de las colecciones para ver si tienen un tamaño eficiente, u observar el nombre de host y el puerto asociados con una ConnectException para ver qué conexión estaba intentando crear la aplicación.

Observando los campos en un objeto con el Inspector

Cuando un objeto es seleccionado en Memory Analyzer, la vista Inspector muestra la información disponible relacionada con ese objeto, incluyendo la jerarquía de clase, los atributos y los estáticos. El panel Attributes muestra los campos y valores de instancia asociados con el objeto, y el panel Statics muestra los campos y valores estáticos asociados con la clase.

La vista Inspector mostrada en la Figura 2 para un objeto java.net.URL simple le permite ver los detalles sobre ese objeto, incluyendo el tipo de protocolo para el que es la URL y el destino:

Figura 2. Los paneles Statics, Attributes y Class Hierarchy en la vista Inspector
Los paneles Statics, Attributes y Class Hierarchy en la vista Inspector

En la Figura 2, puede ver en el panel Attributes que el objeto de URL se refiere a un archivo JAR (el campo de protocolo) ubicado en el sistema de archivos local (en la ubicación especificada por la ruta y los campos de archivo).

Ejecutando consultas en los objetos usando OQL

OQL puede ser usado para consultar un volcado usando consultas personalizadas de tipo SQL. Este tema puede ser un artículo en sí mismo, así que sólo resaltaremos algunos ejemplos. Para obtener más detalles, consulte el contenido de Ayuda en OQL disponible en Memory Analyzer.

OQL es particularmente útil para seguir una ruta hacia las referencias y campos de salida de un conjunto de objetos para un campo particular. Por ejemplo, si la clase A tiene un campo foo de tipo B, y la clase B tiene un campo bar que es una cadena de caracteres, entonces una simple consulta para encontrar todas esas cadenas de caracteres sería:

SELECT aliasA.foo.bar.toString()
FROM A aliasA

Le damos a la clase A un alias, aliasA, el cual después referenciamos en la cláusula SELECT . Esta consulta estrictamente selecciona sólo en las instancias de la clase A. Si queremos seleccionar en todas las instancias de la clase A así como en cualquier subclase, usamos:

SELECT aliasA.foo.bar.toString()
FROM INSTANCEOF A aliasA

Este es un ejemplo más complicado con DirectByteBuffers:

SELECT k, k.capacity
FROM java.nio.DirectByteBuffer k
WHERE ((k.viewedBuffer=null)and(inbounds(k).length>1))

En este caso, queremos obtener el campo de capacidad de cualquier DirectByteBuffer, que da la memoria nativa contenida por ese objeto. También queremos filtrar cualquier DirectByteBuffer que tenga un campo viewedBuffer nulo ( ya que estos son sólo vistas en otros DirectByteBuffers) y más de una referencia de entrada (de forma que no tengamos que observar a aquellos pendientes de limpieza con su referencia fantasma — esto es, que sólo queremos DirectByteBuffers "en vivo").

Ejecutando comparaciones entre vistas o volcados

Con Memory Analyzer, puede comparar tablas generadas por consultas. Las tablas pueden ser del mismo volcado, permitiéndole ver qué objetos de cadena de caracteres de una vista están presentes en un objeto de colección visualizado en otra vista, o en volcados separados, permitiéndole observar los cambios en los datos como, por ejemplo, el crecimiento de colecciones de objetos.

Para ejecutar una comparación, usted añade las tablas relevantes a la Bandeja de Comparación y después solicita que las entradas en la bandeja sean comparadas. Primero encuentre y seleccione la entrada para la tabla en el Historial de Navegación, después seleccione Add to Compare Basket en el menú contextual, como se muestra en la Figura 3:

Figura 3. Añadiendo tablas desde la vista Navigation History en la Bandeja de Comparación
Añadiendo tablas desde la vista Navigation History en la Bandeja de Comparación

Una vez que tiene dos entradas en la Bandeja de Comparación, puede ejecutar la comparación usando el botón Compare-the-results (el signo de exclamación rojo) en la esquina superior derecha del panel, como se muestra en la Figura 4:

Figura 4. Comparando los resultados de las entradas en la Bandeja de Comparación
Comparando los resultados de las entradas en la Bandeja de Comparación

Eficiencia de huella y memoria

Otro uso importante de Memory Analyzer es encontrar qué componentes están usando la mayoría del almacenamiento dinámico, aún en situaciones si una fuga de memoria. Si el uso de memoria puede ser reducido, entonces la capacidad o el rendimiento del sistema pueden ser mejorados, permitiendo más sesiones o menos tiempo gastado en la recolección de basura.

El informe Top Components es el primer paso. Divide el uso de memoria por componentes en el sistema, analiza el uso en cada componente y busca prácticas de desperdicio. Los objetos dominados (retenidos) por otro objeto se puede decir que pertenecen a ese dominador. El informe Top Components lista todos los objetos que no pertenecen a otro objeto. Estos son los dominadores superiores del almacenamiento dinámico. Los dominadores superiores son entonces divididos por cargadores de clases usando la clase de los objetos, y todos esos dominadores superiores y objetos que les pertenezcan son asignados a los cargadores de clases apropiados. Usted analiza más al seleccionar uno de los cargadores de clases en el informe para abrir un nuevo informe de componente específico del cargador de clases.

Para cada componente, los objetos de Colecciones son analizados. Las clases de Colecciones, como es mostrado por java.util.*, son excelentes para ahorrar tiempo a los programadores, proporcionando implementaciones bien probadas de listas, conjuntos y correlaciones. La aplicación promedio puede tener millones de colecciones, así que el espacio desperdiciado en las colecciones puede ser significativo.

Las colecciones vacías son una causa común de memoria desperdiciada. ArrayLists, Vectors, HashMaps y HashSets son creados con una matriz de respaldo de tamaño predeterminado de quizá 10 entradas, lista para alojar entradas. Es sorprendentemente común en aplicaciones que una colección sea creada pero que ningún objeto se almacene en ella. Esto puede consumir memoria rápidamente. Por ejemplo, con 100.000 colecciones vacías, las matrices de respaldo solas podrían consumir 100.000 * (24+10*4) bytes = 6MB.

El informe Empty Collection observa las clases de colección estándar y las extensiones y de las mismas y las analiza por tamaño de colección. Después produce una tabla para cada colección ordenada por tamaño de colección, con el tamaño más frecuente primero. Si una proporción grande de instancias de un tipo de colección están vacías, entonces el informe señala esto como un posible desperdicio de memoria.

Una solución es retrasar la asignación de la colección hasta que se necesite insertar una entrada. Otra es asignar una colección con un tamaño predeterminado de 0 o 1, permitiéndole crecer si es requerido con algún costo del tiempo de ejecución. Una tercera solución es recortar el tamaño de la colección después de que la fase de inicialización esté completa.

Un área relacionada son las colecciones con sólo algunas entradas y mucho espacio desperdiciado. La sección Collection Fill Ratio muestra, para cada tipo de colección, el número de instancias de esas colecciones con una relación de llenado particular. Esto revela colecciones con una gran proporción de espacio vacío.

Series duplicadas

Las series y las matrices de carácter ocupan una gran cantidad de espacio en una aplicación empresarial típica, por lo que son otra área que vale la pena analizar. Esta sección del informe de componente analiza las series para contenido común. Las series son inmutables. Las series que son constantes con el mismo valor son garantizadas por la especificación de la VM para usar la misma instancia. Las series construidas dinámicamente no tienen dicha garantía, y dos cadenas de caracteres construidas al, por ejemplo, leer datos de una base de datos o disco que tengan el mismo valor tendrán instancias separadas y matrices de carácter de respaldo separadas. Si estos valores son mantenidos, entonces esto puede ser significativo.

Puede solucionar este problema al usar String.intern() o mantener un conjunto de hash de usuario o una correlación de hash.

Matrices de carácter desperdiciadas

String.substring() se implementa en el lenguaje de Java al construir una nueva cadena de caracteres compartiendo la matriz de carácter original. Esto es eficiente si la cadena de caracteres original sigue siendo necesaria. Si sólo una subserie pequeña es necesaria, entonces — ya que toda la matriz de carácter es retenida — algún espacio es desperdiciado. La consulta Waste In Char Arrays muestra la cantidad de espacio en matrices de carácter que son sólo referenciadas por series.


Paquetes de Eclipse y jerarquía de cargadores de clases

Las aplicaciones modernas están divididas en componentes, frecuentemente basados en cargadores de clases, para proporcionar algún grado de aislamiento entre las partes de la aplicación. Los componentes son actualizados al detener el uso de un cargador de clase de componente y cargar una nueva versión del componente usando un nuevo cargador de clase. Con el tiempo, la versión antigua es liberada por la recolección de basura, asumiendo que la aplicación no contiene referencias externas a clases u objetos o al cargador de clase.

La consulta Class Loader Explorer muestra todos los cargadores de clases en el sistema, y así funciona para todas las aplicaciones. Muestra las clases cargadas por un cargador de clases y también la cadena padre del cargador de clases, de forma que los problemas de carga de clases puedan ser entendidos. Al inspeccionar, puede ver si existen múltiples copias de un cargador de clases. Si a un lado no hay clases definidas por un cargador de clases, entonces es probable que el cargador de clases esté inactivo.

La consulta de clases duplicadas muestra nombres de clases que han sido cargadas por múltiples cargadores de clases. Esto puede ser un indicativo de una fuga de memoria de cargador de clases. Sólo se necesita una referencia a un objeto alojado en otra parte en el sistema, como en un registro, para que ocurra una fuga de cargador de clases. El objeto aloja una referencia de su clase, y la case del cargador de clases, y el cargador de clases de todas las clases definidas.

Una infraestructura de carga de clases común es la infraestructura de OSGi. Una implementación es Eclipse Equinox, usada para aplicaciones basadas en Eclipse para separar los plug-ins y también usada para WebSphere® Application Server 6.1 y versiones posteriores. Al intentar entender el estado de una aplicación, es útil saber el estado de todos los paquetes. La consulta Eclipse Equinox Bundle Explorer, mostrada en la Figura 5, hace precisamente eso:

Figura 5. Eclipse Bundle Explorer
Eclipse Bundle Explorer

Un volcado de sistema o de HPROF tiene todos los objetos y campos. El Bundle Explorer muestra todos los paquetes en el sistema, junto con sus estados y dependencias, dependientes y servicios. Puede mostrar paquetes que están inesperadamente activos y así usar más recursos.


Uso de datos de hebra

Como se indica en la Tabla 1, un volcado puede incluir detalles de hebra que pueden proporcionar información exclusiva sobre lo que está sucediendo al momento del volcado. Esto puede incluir todas las pilas de hebras activas, todas las tramas para cada hebra y, lo más importante, algunos o todos los locales de Java activos en esas tramas.

Vista Thread Overview

La vista Thread Overview, mostrada en la Figura 6, muestra todas las hebras en la JVM así como los diversos atributos de esa hebra, tales como su tamaño de almacenamiento dinámico retenido, el cargador de clases contextual, la prioridad, el estado y el ID nativo:

Figura 6. Thread Overview
Thread Overview

El tamaño de almacenamiento dinámico retenido es particularmente útil en casos en los cuales no ocurre un problema de almacenamiento dinámico de Java per se durante un OutOfMemoryError, pero la suma de los almacenamientos dinámicos retenidos de las hebras es "demasiado". En este caso, el tamaño de la JVM puede ser demasiado pequeño, los tamaños del agrupamiento de hebras pueden ser muy grandes, o el promedio o el máximo de "carga" de almacenamiento dinámico de Java de una hebra es muy alto.

Vista Thread Stacks

La vista Thread Stacks, mostrada en la Figura 7, muestra todas las hebras, su pila, sus tramas de pila y los locales de Java en esas tramas de pila:

Figura 7. Vista Thread Stacks
Vista Thread Stacks

Vista Thread Details

En las vistas Thread Overview y Thread Stacks, puede hacer clic con el botón derecho en una hebra y seleccionar Thread Details ya sea en la parte superior del menú o a través de Java Basics > Thread Details. Esta vista le da información más detallada, tal como, si está disponible, la pila nativa.

En el ejemplo en la Figura 7, una hebra del tipo java.lang.Thread y con el nombre main — la hebra principal en un programa de línea de comandos simple — es expandida. Cada trama de pila de la hebra es mostrada, y aquellas con locales de Java disponibles son expandibles. En este caso, una cadena de caracteres ha sido pasada como un argumento de Play.method1 a Play.method2, y el contenido de la cadena de caracteres, user1, es resaltado en el círculo rojo. Usted puede imaginar el poder de ser capaz de reconstruir o de realizar la ingeniería inversa de lo que estaba sucediendo al momento del volcado con base en lo que estaba sucediendo en cada trama de pila de hebra y en cuáles objetos.

Note que debido a la optimización del tiempo de ejecución, no todos los objetos relacionados tales como los parámetros de método o instancias de objeto estarán disponibles (aunque esos objetos estarán en el volcado), pero aquellos objetos en los que se esté "trabajando" activamente normalmente sí lo estarán.


Análisis de excepción

Cuando las excepciones son generadas en la aplicación, las complicaciones adicionales pueden hacer más difícil analizar la causa de la excepción. Dos ejemplos de este dilema son:

  • El uso de mecanismos de registro cronológico significa que la excepción está perdida o que el mensaje de excepción está eliminado.
  • La excepción está produciendo un mensaje que contiene información insuficiente.

En el primer caso, el mensaje de excepción o la excepción completa están completamente perdidos, haciendo difícil saber que el problema existe u obtener la información básica sobre él. En el segundo caso, la excepción ha sido registrada y el mensaje de excepción y el rastreo de pila están disponibles, pero no contienen la información necesaria para solucionar la causa de la excepción.

Ya que Memory Analyzer tiene acceso a los campos dentro de los objetos, es posible encontrar el mensaje de excepción desde el objeto de excepción. En algunos casos, también es posible extraer datos adicionales que no están en la excepción original.

Ubicando excepciones en el volcado de instantánea

Una forma de ubicar las excepciones presentes en el volcado de instantánea es usar la posibilidad de OQL en Memory Analyzer para ubicar objetos de interés en el volcado. Por ejemplo, esta consulta encuentra todos los objetos de excepción:

SELECT * 
FROM INSTANCEOF java.lang.Exception exceptions

Esta siguiente consulta produce una lista de todas las excepciones, desde donde puede usar la vista Inspector para observar los campos dentro de cada excepción. Sabiendo que el campo que contiene el mensaje de excepción es el campo detailMessage , también puede modificar la consulta para extraer los mensajes de excepción directamente y mostrarlos inmediatamente como parte de la tabla de resultados:

SELECT exceptions.@displayName, exceptions.detailMessage.toString() 
FROM INSTANCEOF java.lang.Exception exceptions

La consulta anterior produce la salida mostrada en la Figura 8:

Figura 8. Salida de la consulta de OQL para excepciones, incluyendo mensajes de excepción
Salida de la consulta de OQL para excepciones, incluyendo mensajes de excepción

La Figura 8 muestra todas las excepciones que siguen presentes en la aplicación y el mensaje que sería mostrado cuando la excepción fuera lanzada.

Extrayendo información adicional relacionada con las excepciones

Aunque encontrar el objeto de excepción del volcado le permite recuperar el mensaje de excepción, algunas veces el mensaje de excepción es demasiado genérico o vago para permitirle entender la causa del problema. Un buen ejemplo es la java.net.ConnectException. Al intentar hacer una conexión de socket con un host que no está aceptando conexiones, recibe el siguiente mensaje:

java.net.ConnectException: Connection refused: connect
     at java.net.PlainSocketImpl.socketConnect(Native Method)
     at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:352)
     at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:214)
     at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:201)
     at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:377)

     at java.net.Socket.connect(Socket.java:530)
     at java.net.Socket.connect(Socket.java:480)
     at java.net.Socket.(Socket.java:377)
     at java.net.Socket.(Socket.java:220)

Este mensaje es suficiente si tiene acceso al código que está creando el socket y puede ver en el código qué nombre de host y puerto están siendo usados. En el caso de un código más complejo en el cual los valores del nombre de host y el puerto estén sujetos a cambios debido a que son obtenidos de orígenes externos (valores de entrada de usuario, bases de datos y similares), el mensaje no le ayuda a entender por qué la conexión está siendo rechazada.

El rastreo de pila debe incluir un objeto de socket que contenga los datos útiles, y si podemos encontrar el objeto de socket en el volcado de instantánea usando Memory Analyzer, entonces podemos descubrir qué nombre de host y qué puerto rechazaron la conexión.

La forma más fácil de hacer esto es generar el volcado cuando la excepción es lanzada. Esto puede realizarse en los tiempos de ejecución de IBM usando la siguiente opción -Xdump :

-Xdump:system:events=throw,range=1..1,
       filter=java/net/ConnectException#java/net/PlainSocketImpl.socketConnect

Esta opción genera un volcado de sistema de IBM en la primera ocurrencia de una ConnectException generada por el método PlainSocketImpl.socketConnect() .

Después de cargar el volcado de instantánea generado en Memory Analyzer, podemos usar la opción Open Query Browser > Java Basics > Thread Stacks para listar las hebras y los objetos asociados con cada método en el rastreo de pila de las hebras.

Al expandir la hebra actual y las tramas de método en la hebra, puede observar los objetos asociados con esos métodos. En el caso de una java.net.ConnectException, el método más interesante es java.net.Socket.connect(). Expandir esta trama de método muestra una referencia a un objeto java.net.Socket en la memoria. Estas es la conexión de socket que estamos intentando hacer.

Cuando el objeto de Socket es seleccionado, los campos son mostrados en la vista Inspector, como puede ver en la Figura 9:

Figura 9. Vista Inspector para el objeto de Socket
Vista Inspector para el objeto de Socket

La información en la Figura 9 no es muy útil, debido a que la implementación real del Socket está en el campo impl . Puede inspeccionar el contenido del objeto impl al expandir el objeto de Socket y seleccionar la línea impl java.net.SocksSocketImpl en el panel principal o al hacer clic con el botón derecho en el campo impl en la vista Inspector y seleccionar Go Into. Ahora los campos para SocksSocketImpl están visibles en la vista Inspector, como se muestra en la Figura 10:

Figura 10. Vista Inspector para el objeto SocksSocketImpl
Vista Inspector para el objeto SocksSocketImpl

La vista en la Figura 10 da acceso a los campos address y port . En este caso, el puerto es 100, pero el campo address apunta al objeto java.net.Inet4Address . Siguiendo el mismo proceso para observar los campos de los objetos Inet4Address se muestran los resultados visualizados en la Figura 11:

Figura 11. Vista Inspector para el objeto Inet4Address
Vista Inspector para el objeto Inet4Address

Puede ver que hostName está establecido como baileyt60p.


Consejos prácticos

Estos son algunos consejos prácticos que pueden ser útiles:

  • No olvide que Memory Analyzer mismo puede quedarse sin memoria. Para el Eclipse MAT, edite -Xmx en el archivo MemoryAnalyzer.ini. Para la versión de, edite el archivo ISA Install/rcp/eclipse/plugins/com.ibm.rcp.j2se.../jvm.properties.
  • Si se sigue quedando sin memoria en una versión de 32-bits de Memory Analyzer, use la versión de 64-bits de Eclipse MAT o intente el modo sin cabeza (vea Recursos). (La herramienta ISA actualmente no soporta 64-bits.)
  • Memory Analyzer graba archivos de "intercambio" en el directorio del volcado, lo que reduce el tiempo de recarga del volcado. Estos archivos pueden ser comprimidos, enviados a otra máquina y colocados en el mismo directorio que el volcado, haciendo una recarga completa del volcado innecesario.
  • Si el tamaño de un volcado no se correlaciona con el recolector de basura al momento del volcado, consulte el enlace Unreachable Objects Histogram en la pestaña Overview. El almacenamiento dinámico de Java puede tener mucha basura (por ejemplo, si una colección titular no se ha ejecutado durante algún tiempo) que Memory Analyzer eliminó.
  • Si dos objetos A y B no tienen referencias directas unos con otros, pero ambos tienen referencias de salida hacia algún conjunto de objetos C, entonces el Almacenamiento Dinámico Retenido del conjunto C no será incluido en ninguno de los conjuntos retenidos de A o B, sino en el conjunto retenido del dominador de A y B. En algunas situaciones, B puede estar observando temporalmente el conjunto C, que es en realidad el descendiente de A. En este caso, puede hacer clic con el botón derecho en A y seleccionarJava Basics > Customized Retained Set y usar la dirección de B como el parámetro de exclusión (-x).
  • Puede cargar múltiples volcados a la vez y compararlos. Abra el Histograma del volcado más reciente, haga clic en el botón Compare en la parte superior y elija el volcado de línea de base.
  • Cuando está explorando un árbol de referencia, tenga en mente que las referencias pueden referirse, directa o indirectamente, a una referencia "padre", de forma que puede entrar en un bucle o ciclo de exploración (por ejemplo, en una lista enlazada). Esté al pendiente de las direcciones de objetos. También considere que si el nombre de clase de un objeto está precedido por la palabra class, entonces está explorando la instancia estática de esa clase.
  • El valor de cadena de caracteres mostrado en la mayoría de las vistas está limitado a 1.024 caracteres. Si necesita toda la cadena de caracteres, haga clic en el objeto y seleccione Copy > Save value to file.
  • La mayoría de las vistas tienen una opción de exportación, y la mayoría de los resultados de HTML son creados en el sistema de archivos, de forma que los datos pueden ser exportados para compartirse o para una mayor transformación. Repetidamente, puede presionar Ctrl+C en cualquier selección de filas en una malla para copiar una representación textual de esas filas en su área común.

Memory Analyzer fue originalmente desarrollado como un "analizador de almacenamiento dinámico de Java rápido y rico en dispositivos que le ayuda a encontrar fugas de memoria y reducir el consumo de memoria", como es descrito en Eclipse.org. Pero sus posibilidades claramente van más allá de esa descripción. Además de su rol para diagnosticar problemas de memoria "normales", los volcados de instantánea pueden ser usados como una alternativa para, o en adición a, otros tipos de técnicas de determinación de problemas tales como el rastreo y los parches. Particularmente con los volcados de HPROF y con los volcados de sistema de IBM, Memory Analyzer le da contenido de memoria tal como primitivos y los nombres de campo del código de origen original. Usando las diversas vistas cubiertas en este artículo, puede explorar o hacer ingeniería inversa del problema que tiene a la mano, incluyendo la huella general y la eficiencia de memoria, paquetes de Eclipse y relaciones de cargador de clases, uso de datos de hebra y locales de trama de pila, excepciones y más. OQL y modelo de plug-in de Memory Analyzer también le permiten inspeccionar el volcado más fácilmente usando un lenguaje de consulta y métodos programáticos que pueden ayudarle en la automatización del análisis común.

Recursos

Aprender

Obtener los productos y tecnologías

Comentar

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=tecnologia Java
ArticleID=813860
ArticleTitle=Depurando desde volcados
publish-date=05162012