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.
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.
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
Source:
https://dzone.com/articles/java-container-application-memory-analysis