Maneiras diferentes de capturar despejos de heap Java
1. Introdução
Neste artigo, mostraremos diferentes maneiras de capturar um despejo de heap em Java.
A heap dump is a snapshot of all the objects that are in memory in the JVM at a certain moment. Eles são muito úteis para solucionar problemas de vazamento de memória e otimizar o uso de memória em aplicativos Java.
Heap dumps are usually stored in binary format hprof files. Podemos abrir e analisar esses arquivos usando ferramentas como jhat ou JVisualVM. Além disso, para usuários do Eclipse, é muito comum usarMAT.
Nas próximas seções, veremos várias ferramentas e abordagens para gerar um despejo de heap e mostraremos as principais diferenças entre elas.
2. Ferramentas JDK
O JDK vem com várias ferramentas para capturar despejos de heap de maneiras diferentes. All these tools are located under the bin folder inside the JDK home directory. Portanto, podemos iniciá-los a partir da linha de comando, desde que esse diretório esteja incluído no caminho do sistema.
Nas próximas seções, mostraremos como usar essas ferramentas para capturar despejos de heap.
2.1. jmap
O jmap é uma ferramenta para imprimir estatísticas sobre a memória em uma JVM em execução. Podemos usá-lo para processos locais ou remotos.
Para capturar um despejo de heap usando jmap, precisamos usar a opçãodump:
jmap -dump:[live],format=b,file=
Junto com essa opção, devemos especificar vários parâmetros:
-
live: se configurado, ele apenas imprime objetos que possuem referências ativas e descarta aqueles que estão prontos para serem coletados no lixo. Este parâmetro é opcional
-
format=b: especifica que o arquivo de despejo estará no formato binário. Se não definido, o resultado é o mesmo
-
file: o arquivo onde o dump será gravado
-
pid: id do processo Java
Um exemplo seria assim:
jmap -dump:live,format=b,file=/tmp/dump.hprof 12587
Lembre-se de que podemos obter facilmente opid de um processo Java usando o comandojps.
Keep in mind thatjmap was introduced in the JDK as an experimental tool and it’s unsupported. Portanto, em alguns casos, pode ser preferível usar outras ferramentas.
2.2. jcmd
O jcmd é uma ferramenta muito completa que funciona enviando solicitações de comando para a JVM. Temos que usá-lo na mesma máquina em que o processo Java está em execução.
One of its many commands is the GC.heap_dump. Podemos usá-lo para obter um despejo de heap apenas especificandopid do processo e o caminho do arquivo de saída:
jcmd GC.heap_dump
Podemos executá-lo com os mesmos parâmetros que usamos anteriormente:
jcmd 12587 GC.heap_dump /tmp/dump.hprof
Como no jmap, o dump gerado está no formato binário.
2.3. JVisualVM
JVisualVM is a tool with a graphical user interface that lets us monitor, troubleshoot and profile Java applications. A GUI é simples, mas muito intuitiva e fácil de usar.
Uma de suas muitas opções nos permite capturar um despejo de heap. Se clicarmos com o botão direito do mouse em um processo Java e selecionarmos a opção“Heap Dump”, a ferramenta criará um despejo de heap e o abrirá em uma nova guia:
Observe que podemos encontrar o caminho do arquivo criado na seção“Basic Info”.
3. Capture um Heap Dump Automaticamente
Todas as ferramentas que mostramos nas seções anteriores têm como objetivo capturar despejos de heap manualmente em um momento específico. Em alguns casos, queremos obter um despejo de heap quando ocorre umjava.lang.OutOfMemoryError, para que nos ajude a investigar o erro.
Para esses casos,Java provides the HeapDumpOnOutOfMemoryError command-line option that generates a heap dump when a java.lang.OutOfMemoryError is thrown:
java -XX:+HeapDumpOnOutOfMemoryError
Por padrão, ele armazena o dump em um arquivojava_pid<pid>.hprof no diretório onde estamos executando o aplicativo. Se quisermos especificar outro arquivo ou diretório, podemos defini-lo na opçãoHeapDumpPath:
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=
Quando nosso aplicativo ficar sem memória usando esta opção, poderemos ver nos registros o arquivo criado que contém o despejo de heap:
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
Dumping heap to java_pid12587.hprof ...
Exception in thread "main" Heap dump file created [4744371 bytes in 0.029 secs]
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at com.example.heapdump.App.main(App.java:7)
No exemplo acima, ele foi gravado no arquivojava_pid12587.hprof.
Como podemos ver, esta opção é muito útil ethere is no overhead when running an application with this option. Therefore, it’s highly recommended to use this option always, especially in production.
Finalmente,this option can also be specified at runtime by using the HotSpotDiagnostic MBean. Para fazer isso, podemos usar JConsole e definir a opção de VMHeapDumpOnOutOfMemoryError paratrue:
Podemos encontrar mais informações sobre MBeans e JMX nestearticle.
4. JMX
A última abordagem que cobriremos neste artigo é o uso de JMX. We’ll use the HotSpotDiagnostic MBean que apresentamos brevemente na seção anterior. This MBean provides a dumpHeap method que aceita 2 parâmetros:
-
outputFile: o caminho do arquivo para o dump. O arquivo deve ter a extensão hprof
-
live: se definido como verdadeiro, ele despeja apenas os objetos ativos na memória, como vimos com jmap antes
Nas próximas seções, mostraremos 2 maneiras diferentes de invocar esse método para capturar um despejo de heap.
4.1. JConsole
A maneira mais fácil de usar o MBeanHotSpotDiagnostic é usando um cliente JMX como o JConsole.
Se abrirmosJConsolee conectarmos a um processo Java em execução, operaçõeswe can navigate to the MBeans tab and find the HotSpotDiagnostic under*com.sun.management*. In, podemos encontrar o métododumpHeap que descrevemos antes:
Conforme mostrado, precisamos apenas introduzir os parâmetrosoutputFileelive nos campos de textop0ep1 para realizar a operaçãodumpHeap.
4.2. Forma programática
A outra maneira de usar o MBeanHotSpotDiagnostic é invocando-o programaticamente a partir do código Java.
Para fazer isso, primeiro precisamos obter uma instânciaMBeanServer para obter um MBean que está registrado no aplicativo. Depois disso,we simply need to get an instance of a HotSpotDiagnosticMXBean and call its dumpHeap method.
Vamos ver no código:
public static void dumpHeap(String filePath, boolean live) throws IOException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(
server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap(filePath, live);
}
Notice that an hprof file cannot be overwritten. Portanto, devemos levar isso em consideração ao criar um aplicativo que imprime despejos de heap. Se não o fizermos, teremos uma exceção:
Exception in thread "main" java.io.IOException: File exists
at sun.management.HotSpotDiagnostic.dumpHeap0(Native Method)
at sun.management.HotSpotDiagnostic.dumpHeap(HotSpotDiagnostic.java:60)
5. Conclusão
Neste tutorial, mostramos várias maneiras de capturar um despejo de heap em Java.
Como regra geral, devemos nos lembrar de usar a opçãoHeapDumpOnOutOfMemoryError sempre que executar aplicativos Java. Para outros fins, qualquer uma das outras ferramentas pode ser perfeitamente usada, desde que tenhamos em mente o status não suportado do jmap.
Como sempre, o código-fonte completo dos exemplos está disponívelover on GitHub.