Capture y analice el volcado de pila de Java
- Introducción al volcado de almacenamiento dinámico y sus formatos
-
Código de ejemplo que causa
OutOfMemoryError
en Java - Diferentes formas de capturar el volcado de almacenamiento dinámico
- Analizar 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:
- El formato de volcado de pila portátil (también conocido como formato PHD)
- 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.
jmap
JVisualVM
jcmd
- Generar volcado de pila automáticamente
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.
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.
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.
Tamaño de pila poco profunda
: la pila poco profunda de un objeto es su tamaño en la memoria.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
.