Análise de Memória de Aplicativos Container em Java

Para aplicações em contêineres, é difícil identificar problemas decorrentes do uso excessivo de memória. Caso o uso ultrapasse o limite de memória do contêiner, uma aplicação pode falhar silenciosamente sem deixar vestígios.

Neste artigo, vou abordar algumas técnicas que podem ser utilizadas para identificar a origem do consumo de memória em uma aplicação em contêiner Java.

Tipo de Memória

Em um aplicativo Java típico, a memória pode ser amplamente dividida em heap e não-heap. A memória heap pode ser definida fornecendo parâmetros relevantes do JVM ao iniciar qualquer aplicativo Java.

A memória não-heap consiste em memória nativa utilizada pelo JVM ou por qualquer biblioteca usada dentro do aplicativo usando a JNI (Java Native Interface).

Método

Para a memória heap, pode-se fazer um heap dump e analisá-lo usando ferramentas de análise de heap dump. Uma das melhores ferramentas para análise de heap dump é o eclipse MAT.

O Java fornece um mecanismo para rastrear a alocação de memória nativa ativando a rastreamento de memória nativa, mas pode não revelar toda a memória alocada por bibliotecas nativas.

Jemalloc é uma ferramenta que pode ser usada para rastrear a memória alocada por bibliotecas nativas. A memória nativa é alocada usando um alocador de memória padrão chamado malloc. Jemalloc é uma implementação genérica de malloc com a qual a rastreabilidade da alocação de memória pode ser habilitada. Ele rastreia toda a alocação de memória nativa e gera dumps de perfil de heap.

Esses perfis de heap podem então ser analisados usando a Jeprof utility. Jeprof gera o relatório de alocação de heap, destacando a memória usada por funções no aplicativo.

Análise

Abaixo está uma análise de memória de um aplicativo Java de contêiner de amostra. O aplicativo carrega um modelo de Tensorflow de amostra para habilitar a utilização de memória nativa e roda em um contêiner Docker.

Abaixo está o consumo de memória do Docker. Mostra 254MB. Vamos tentar identificar a fonte do consumo de memória.

Memória Total

Para ter uma noção da memória total sendo usada pelo processo do aplicativo, podemos verificar o Resident Set Size (RSS). É a memória total comprometida que reside na memória principal ou RAM. Existem várias utilidades que podem ajudar a verificar isso, como top, ps ou pmap.

Verificar o RSS não ajuda a identificar a fonte raiz do consumo. Para o aplicativo de exemplo, usando o comando abaixo, o RSS total é de 376MB.

Shell

 

ps --no-header -o rss $(pidof java)

Análise do Heap

Abaixo está o consumo de memória heap gerado pela ferramenta Eclipse MAT. O heap retido total é mostrado como 2,2MB, o que está muito abaixo do consumo total de memória mostrado pelo Docker e indica que a maioria do consumo vem da área não-heap.

Análise de Memória Nativa

Ao revisar o resumo da memória nativa usando o comando abaixo, o uso total de memória parece ser aproximadamente 99MB. No entanto, esse valor é menor que o consumo total de memória e não identifica com precisão a causa raiz do problema.

Shell

 

jcmd $(pidof java) VM.native_memory \
    | grep -P "Total.*committed=" \
    | grep -o -P "(?<=committed=)[0-9]+(?=KB)"

Análise de Memória Off-Heap

Uma análise usando Jemalloc e Jeprof revela que o uso de memória nativa é principalmente atribuído à biblioteca Tensorflow, com um consumo total de aproximadamente 112MB.

Essa informação fornece uma indicação clara da fonte do uso de memória nativa e pode ser investigada ainda mais para minimizar qualquer consumo excessivo.

Conclusão

A análise de memória Java é crítica, especialmente para aplicativos baseados em contêineres. Saber a fonte do consumo de memória em um aplicativo pode nos ajudar a entender o requisito de memória e reduzir os custos do aplicativo removendo qualquer consumo desnecessário.

Ao verificar o consumo de memória, todas as tipos de memória e suas fontes precisam ser identificadas. A análise de dump de heap pode identificar as fontes de consumo de memória heap, e Jemalloc e Jeprof são úteis na identificação das fontes de consumo de memória nativa.

Link do Código de Aplicação de Exemplo

https://github.com/parveensaini/JavaContainerMemoryAnalysis

Source:
https://dzone.com/articles/java-container-application-memory-analysis