Capture y analice el volcado de pila de Java

Mehvish Ashiq 15 febrero 2024
  1. Introducción al volcado de almacenamiento dinámico y sus formatos
  2. Código de ejemplo que causa OutOfMemoryError en Java
  3. Diferentes formas de capturar el volcado de almacenamiento dinámico
  4. Analizar volcado de pila de Java
Capture y analice el volcado de pila de Java

Los volcados de almacenamiento dinámico contienen una instantánea de todos los objetos activos que utiliza la aplicación Java en ejecución en un almacenamiento dinámico de Java. Este tutorial educa sobre el volcado de almacenamiento dinámico, sus diversos formatos y su importancia.

Además, veremos un ejemplo que demuestra OutOfMemoryError, que conducirá a varios enfoques para capturar el volcado de almacenamiento dinámico y una herramienta para analizarlo.

Introducción al volcado de almacenamiento dinámico y sus formatos

Un montón contiene todos los objetos que creamos al instanciar una clase. Cada clase de tiempo de ejecución de Java también se crea en este montón.

Este montón se crea cuando se inicia JVM (Java Virtual Machine) y puede expandirse/reducirse durante el tiempo de ejecución para ajustar los objetos destruidos o creados en una aplicación.

El proceso de recolección de basura se ejecuta cada vez que se llena un montón, este proceso recopila todos los objetos que ya no se usan, o podemos decir que ya no se hace referencia a ellos (puede encontrar más en administración de memoria aquí. Por lo general, los volcados de montón se almacenan en el Archivos en formato binario hprof.

Podemos recuperar información detallada sobre cada instancia de objeto, como tipo, nombre de clase, dirección, tamaño y si una instancia contiene referencias a otro(s) objeto(s) o no. Los volcados de almacenamiento dinámico pueden estar en uno de los dos formatos siguientes:

  1. El formato de volcado de pila portátil (también conocido como formato PHD)
  2. El formato clásico

Recuerde que el volcado de almacenamiento dinámico portátil es el formato predeterminado y está en formato binario, que debe procesarse para un análisis posterior. Por otro lado, el formato clásico es en texto ASCII que es legible por humanos.

Puede leer acerca de estos dos formatos aquí.

Importancia de usar el volcado de pila

Por lo general, obtenemos la ventaja del volcado de almacenamiento dinámico cuando una aplicación falla debido a OutOfMemoryError o una aplicación Java que consume más memoria de lo esperado.

El volcado de pila nos ayuda a identificar las causas principales del error y otros detalles, por ejemplo, la cantidad de objetos en cada clase, el uso de memoria para cada clase, etc.

También ayuda a capturar el tamaño de memoria ocupado por cada objeto Java de una aplicación. Toda esta información capturada puede ser útil para encontrar un código real que cause problemas de pérdida de memoria.

Veamos un ejemplo de código que causa OutOfMemoryError, que conducirá a varias formas de capturar el volcado de pila de Java.

Código de ejemplo que causa OutOfMemoryError en Java

import java.util.ArrayList;
import java.util.List;

public class Test {
  public static void main(String[] args) {
    List<byte[]> myList = new ArrayList<>();
    int index = 0;

    while (true) {
      // 1MB each iteration, 1 x 1024 x 1024 = 1048576
      byte[] bytes = new byte[1048576];
      myList.add(bytes);
      Runtime runTime = Runtime.getRuntime();
      System.out.printf("[%d] free memory is: %s%n", index++, runTime.freeMemory());
    }
  }
}

El código anterior seguirá asignando memoria mediante la ejecución del bucle while hasta que se alcance un punto específico en el que la máquina virtual Java no pueda asignar suficiente memoria.

En ese momento, obtendremos el error java.lang.OutOfMemoryError: Java heap space.

...
...
...
[1510] free memory is: 14687728
[1511] free memory is: 12590576
[1512] free memory is: 10493424
[1513] free memory is: 8396272
[1514] free memory is: 6299120
[1515] free memory is: 4201968
[1516] free memory is: 2104320
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at Test.main(Test.java:16)

Aquí es donde necesitamos hacer un análisis de volcado de pila para encontrar las razones que causan OutOfMemoryError. Se puede hacer en dos pasos.

Primero, capture el volcado de montón y luego analice el archivo de volcado de montón para encontrar las razones sospechosas.

Diferentes formas de capturar el volcado de almacenamiento dinámico

Hay varias formas de capturar el volcado de almacenamiento dinámico. Aprendamos lo siguiente uno por uno a continuación.

  1. jmap
  2. JVisualVM
  3. jcmd
  4. Generar volcado de pila automáticamente
  5. JMX

Use jmap para capturar el volcado del montón

La herramienta jmap imprime estadísticas de memoria en una máquina virtual Java (JVM) en ejecución; también podemos usarlo para procesos remotos y locales.

Usamos la opción -dump para capturar el volcado del montón usando la herramienta jmap, que podemos usar desde la carpeta bin del directorio de inicio de JDK.

Sintaxis:

jmap -dump:[live],format=b,file=<file-path> <pid>

Ejemplo:

C:\Program Files\Java\jdk-18\bin> jmap -dump:live,format=b,file=/temp/dump.hprof 12876

A continuación se muestra una breve descripción de las opciones que hemos especificado anteriormente:

Parámetro Descripción
live Este parámetro es opcional; si está configurado, solo imprimirá objetos que tengan referencias activas. Omite aquellos que están listos para ser recolectados como basura.
format=b Se utiliza para especificar el formato en el que se guardará el archivo de volcado. Aquí, usamos b, lo que significa que este archivo de volcado estaría en formato binario. El resultado será el mismo si no se establece este parámetro.
file Es el fichero en el que se escribirá el volcado.
pid Denota una identificación del proceso de Java.

Tenga en cuenta que podemos usar el comando jps para obtener pid. Además, jmap se introdujo como una herramienta experimental en JDK y no es compatible; por lo tanto, en algunas situaciones, es posible que deba buscar otras herramientas en lugar de usar jmap.

Use JVisualVM para capturar el volcado del montón

El JVisualVM es una interfaz gráfica de usuario que nos permite realizar un seguimiento de la creación de perfiles de aplicaciones Java y la resolución de problemas. Es simple, fácil de usar y nos permite capturar un volcado de pila.

Según esto, estaba disponible con Oracle JDK 6, 7 y 8. A partir de JDK 9 o posterior, JVisualVM ya no se proporciona con Oracle JDK; los usuarios tienen que descargarlo por separado si quieren usarlo.

Vamos a descargarlo de visualvm.github.io, extraiga el archivo .zip, ubique visualvm.exe en la carpeta bin, haga doble clic en él, presione el botón Acepto cuando se le solicite, y Verá la siguiente pantalla.

capturar y analizar el volcado de pila de Java - ventana de inicio de visualvm

Todos los procesos de Java que se están ejecutando actualmente se enumerarán en Local. Podemos capturar un volcado de pila seleccionando el proceso Java deseado, haciendo clic derecho sobre él y eligiendo la opción Heap Dump.

Se abrirá una nueva pestaña que muestra toda la información necesaria.

Use jcmd para capturar el volcado del montón

Esta herramienta también se puede encontrar en la carpeta bin del directorio de inicio de JDK; en nuestro caso, es C:\Program Files\Java\jdk-18\bin. El suyo puede ser diferente si no ha instalado Java en la ubicación predeterminada.

El jcmd envía solicitudes de comando a una Máquina Virtual Java. Recuerda que tenemos que usarlo en la misma máquina ejecutando un proceso Java.

Tiene varios comandos, uno de ellos es GC.heap-dump, que usaremos para capturar un volcado de montón especificando la identificación del proceso (pid) y una ruta para un archivo de salida. Consulte la sintaxis del comando jcmd a continuación.

Sintaxis:

jcmd <pid> GC.head_dump <file-path>

Ejemplo:

C:\Program Files\Java\jdk-18\bin> jcmd 12876 GC.head_dump /temp/dump.hprof

Al igual que jmap, también generará el volcado en formato binario.

Capturar volcado de pila automáticamente

Todos los enfoques que hemos aprendido capturan el volcado del montón manualmente en un momento determinado, pero en algunas circunstancias, tenemos que generar un volcado del montón tan pronto como se produce java.lang.OutOfMemoryError.

Aquí, la generación automática de un volcado de pila nos ayudará a investigar un error.

Teniendo en cuenta estos escenarios, Java sirve con HeapDumpOnOutOfMemoryError , una opción de línea de comandos que puede generar un volcado de montón cuando una aplicación lanza java.lang.OutOfMemoryError.

java -XX:+HeapDumpOnOutOfMemoryError

De forma predeterminada, el comando anterior almacenará el volcado en el archivo java_pid<pid>.hprof ubicado donde se ejecuta nuestra aplicación. Podemos especificar un directorio o archivo personalizado y configurarlo en una opción HeapDumpPath.

Sintaxis:

java -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=<file-or-dir-path>

Ejemplo:

java -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=/temp/heapdump.bin

Ahora, podemos ubicar el archivo creado que contiene el volcado de almacenamiento dinámico en los registros de la siguiente manera siempre que nuestra aplicación se quede sin memoria a través de esta opción.

java.lang.OutOfMemoryError: Requested array size exceeds VM limit
During heap to java_pid12876.hprof...
  Exception in thread "main" Head dump file created [4745371 bytes in 0.028 secs]
  java.lang.OutOfMemoryError: Requested array size exceeds VM limit

Podemos ver que el texto anterior está escrito en el archivo java_pid12876.hprof, y no hay sobrecarga al ejecutar nuestra aplicación usando esta opción.

Es bueno usar esta opción para todas las aplicaciones, particularmente en producción, porque nunca sabrá cuándo ocurrirá OutOfMemoryError.

Recuerda que podemos usar esta opción en tiempo de ejecución usando HotSpotDiagnostic MBean. Para eso, se usa JConsole y se establece la opción de VM HeapDumpOnOutOfMemoryError en true.

Use JMX para capturar volcado de pila

En este método, utilizaremos HotSpotDiagnostic MBean, que proporciona un método dumpHeap que acepta los siguientes dos parámetros:

Parámetro Descripción
outputFile Es una ruta de un archivo de salida para volcado; este archivo debe tener una extensión .hprof para contener el volcado.
live Si lo configuramos en true, solo volcará los objetos activos en la memoria, como aprendimos al usar jmap en este tutorial.

Podemos invocarlo de dos maneras para capturar un volcado de pila, invocarlo mediante programación o usar el cliente JMX como JConsole ubicado en la carpeta bin del directorio de inicio de JDK. Usaremos JMX aquí, pero puede aprender cómo invocarlo programáticamente aquí.

Usar HotSpotDiagnostic MBean a través del cliente JMX (JConsole) es la forma más fácil de abrir JConsole, conectarse al proceso Java en ejecución, navegar a la pestaña MBeans y buscar HotSpotDiagnostic en com.sun.management.

Podemos encontrar el método dumpHeap en el menú desplegable Operaciones, donde podemos especificar los parámetros outputFile y live en los campos de texto p0 y p1 para realizar la operación dumpHeap como se muestra a continuación.

capturar y analizar el volcado de pila de Java - mbeans

Ahora es el momento de analizar el volcado del montón de Java.

Analizar volcado de pila de Java

En el volcado de pila de Java, debemos buscar objetos que usan mucha memoria, gráfico de objetos para encontrar objetos que no liberan memoria y objetos alcanzables e inalcanzables.

El Eclipse Memory Analyzer (MAT) es mejor para analizar el volcado de almacenamiento dinámico de Java que generamos anteriormente. Iniciaremos la herramienta Memory Analyzer y abriremos un archivo de volcado de almacenamiento dinámico para hacerlo.

En Eclipse Memory Analyzer (MAT), tendremos dos tipos de tamaños de objetos que se explican brevemente a continuación.

  1. Tamaño de pila poco profunda: la pila poco profunda de un objeto es su tamaño en la memoria.
  2. Tamaño de almacenamiento dinámico retenido: es una cantidad de memoria que se liberará una vez que un objeto se recopile como basura.

Una vez que se abre el archivo de volcado de montón, podemos ver un resumen del uso de memoria de una aplicación. Ahora, podemos averiguar fácilmente qué está causando OutOfMemoryError.

Mehvish Ashiq avatar Mehvish Ashiq avatar

Mehvish Ashiq is a former Java Programmer and a Data Science enthusiast who leverages her expertise to help others to learn and grow by creating interesting, useful, and reader-friendly content in Computer Programming, Data Science, and Technology.

LinkedIn GitHub Facebook

Artículo relacionado - Java Heap