JavaRush /Blogue Java /Random-PT /Um guia para gerenciar memória Java (e salvar seu código)...
pandaFromMinsk
Nível 39
Минск

Um guia para gerenciar memória Java (e salvar seu código)

Publicado no grupo Random-PT
Nota do tradutor: a vontade de traduzir o bilhete surgiu no início de uma manhã de junho, após lê-lo meio adormecido em um vagão do metrô. Público-alvo: pessoas que estão dando os primeiros passos no mundo Java e, pela natureza de sua formação técnica básica ou desejo, estão muito ansiosas para entrar nos bastidores do Java e aprender todos os processos “eletrodinâmicos”. Tenho certeza que para quem ler isto, este será o ponto de partida para uma jornada ao mundo da configuração da JVM e do GC. Vento favorável! Artigo original aqui Como desenvolvedor, você gasta inúmeras horas limpando bugs de um aplicativo Java e obtendo desempenho onde ele precisa estar. Durante o teste, você percebe que o aplicativo fica gradativamente mais lento e, no final, trava completamente ou simplesmente apresenta desempenho ruim. Eventualmente, aceite que ocorrem vazamentos de memória. O coletor de lixo Java faz o possível para lidar com esses vazamentos. Mas há um limite de coisas que podem ser feitas quando confrontados com situações como estas. Você precisa de maneiras de identificar chamadas de vazamento de memória, identificar causas e compreender a função do coletor de lixo Java em afetar o desempenho geral do aplicativo.

Principais sintomas de vazamento de memória Java

Existem vários sintomas que indicam que um aplicativo está com problemas de vazamento de memória. Uma ligeira diminuição no desempenho, em vez de uma falha repentina do aplicativo, indica apenas um vazamento de memória. O problema pode acontecer sempre durante a operação ou apenas quando a aplicação começa a trabalhar com uma grande quantidade de dados ou, ao contrário, você começa a dimensionar a aplicação. O aplicativo provavelmente mostrará um erro de falta de memória quando o vazamento consumir todos os recursos de memória disponíveis. Se você reiniciar o aplicativo e torcer pelo melhor, encontrará travamentos repetidos até que o vazamento seja corrigido. Em geral, vazamentos de memória acontecem quando as referências de objetos se acumulam em vez de liberar memória. Eles ocupam toda a memória disponível e impossibilitam o acesso do aplicativo aos recursos de que necessita.

Erros de configuração aparecendo como vazamentos de memória

Antes de analisar situações que causam problemas de memória Java e realizar uma análise, você precisa ter certeza de que a pesquisa não está relacionada a um problema completamente diferente. Alguns erros de falta de memória ocorrem devido a vários erros, como erros de configuração. O aplicativo pode estar com pouca memória heap ou pode estar em conflito com outros aplicativos no sistema. Se você começar a falar sobre problemas de pouca memória, mas não conseguir descobrir o que está causando o vazamento, dê uma olhada diferente no aplicativo. Você descobrirá que precisa fazer alterações no thread de finalização ou aumentar a quantidade de espaço de geração permanente, que é uma área da memória JVM para armazenar descrições de classes Java e alguns dados adicionais.

Benefícios das ferramentas de monitoramento de memória

As ferramentas de monitoramento de memória fornecem maior visibilidade sobre o uso dos recursos disponíveis por um aplicativo Java. Ao usar este software, você dá um passo no sentido de restringir a busca pela raiz do problema de vazamentos de memória e outros incidentes de desempenho. As ferramentas vêm em diversas categorias e você pode precisar usar vários aplicativos para descobrir como sinalizar adequadamente o problema e o que deu errado, mesmo se estiver lidando com vazamentos de memória. Os arquivos de heap dump fornecem as informações necessárias para analisar a memória Java. Nesse caso, você precisa usar duas ferramentas: uma para gerar um arquivo dump e outra para análise detalhada. Esta solução fornece informações detalhadas sobre o que está acontecendo com o aplicativo. Uma vez que a ferramenta identifica os locais de possíveis problemas e trabalha para restringir a área para descobrir a localização exata do incidente. E esse período de tempo é o período mais longo e deprimente de tentativa e erro. O analisador de memória indica vários problemas no seu código, mas você não tem certeza absoluta de quais problemas seu aplicativo está encontrando. Se você ainda encontrar o mesmo erro, recomece e trabalhe em outro erro possível. Faça uma alteração de cada vez e tente duplicar o erro. Você precisará deixar o aplicativo ser executado por algum tempo para duplicar as condições de erro. Se houver um vazamento de memória durante o primeiro teste, certifique-se de testar a carga do aplicativo. Um aplicativo pode funcionar bem com uma pequena quantidade de dados, mas pode gerar novamente os mesmos erros ao trabalhar com uma grande quantidade de dados. Se o mesmo erro ainda ocorrer, você precisará reiniciar e procurar outra causa possível. As ferramentas de monitoramento de memória provam sua utilidade quando o aplicativo está totalmente operacional. Você pode monitorar remotamente o desempenho da JVM e detectar proativamente situações de falha antes que um desenvolvedor se aprofunde no problema e colete dados históricos de desempenho para ajudar a melhorar suas técnicas de programação no futuro e ver como o Java funciona sob carga pesada. Muitas soluções incluem modos de alerta de “perigo” ou outros modos semelhantes para que o desenvolvedor possa saber imediatamente o que está errado. Todo desenvolvedor não deseja que um aplicativo crítico, durante a produção, trave e cause a perda de dezenas ou centenas de milhares de dólares durante o tempo de inatividade do aplicativo, portanto, as ferramentas de monitoramento de memória reduzem o tempo de resposta do desenvolvedor. Os aplicativos de monitoramento de memória permitem iniciar o processo de diagnóstico instantaneamente, em vez de solicitar que você vá até o cliente, onde ninguém lhe dirá exatamente qual erro ocorreu ou qual código de erro o aplicativo gerou. Se você costuma ficar imerso em problemas de memória e desempenho de seu aplicativo Java, mergulhe profundamente no processo de teste. Identifique cada área fraca no processo de desenvolvimento e mude suas estratégias de teste. Consulte colegas e compare suas abordagens de teste com as melhores práticas existentes. Às vezes, você precisa revisar um pequeno trecho de código e garantir um impacto duradouro em todo o aplicativo.

Papel do coletor de lixo na memória Java e vazamentos de memória

O Garbage Collector em Java desempenha um papel fundamental no desempenho do aplicativo e no uso de memória. Ele procura objetos não utilizados (mortos) e os exclui. Esses objetos não ocupam mais memória, portanto seu aplicativo continua garantindo a disponibilidade de recursos. Às vezes, o aplicativo não dá ao GC tempo ou recursos suficientes para remover objetos mortos e eles se acumulam. Você pode se deparar com uma situação em que haja acesso ativo a objetos que você acredita estarem mortos. O coletor de lixo não pode fazer nada sobre isso porque... seu mecanismo automatizado de gerenciamento de memória ignora objetos ativos. Normalmente, o coletor de lixo opera de forma autônoma, mas é necessário ajustar seu comportamento para responder a problemas graves de memória. No entanto, o próprio GC pode causar problemas de desempenho.

Áreas de GC

O coletor de lixo separa os objetos em diferentes áreas para otimizar a montagem. Young Generation apresenta objetos que desaparecem rapidamente. O coletor de lixo costuma trabalhar nessa área desde a hora da limpeza. Os objetos que permanecem vivos após atingir um determinado período são transferidos para a Antiga Geração. Na área da Velha Geração, os objetos permanecem por muito tempo e não são retirados pelo coletor com tanta frequência. No entanto, quando o coletor está em execução no escopo, o aplicativo passa por uma grande operação em que o coletor examina objetos ativos para limpar o lixo. Como resultado, os objetos do aplicativo estão localizados na área final de geração permanente. Normalmente, esses objetos incluem os metadados JVM necessários. O aplicativo não gera muito lixo na Geração Permanente, mas precisa de um coletor para remover classes quando elas não são mais necessárias.

Relação entre Coletor de Lixo e tempo de resposta

O coletor de lixo, independentemente da prioridade de execução dos threads do aplicativo, os interrompe sem aguardar a conclusão. Este fenômeno é chamado de evento “Stop the World”. A região Young Generation do coletor de lixo tem um impacto menor no desempenho, mas os problemas são perceptíveis se o GC estiver fazendo uma limpeza intensiva. Você acaba em uma situação em que o GC secundário da Geração Jovem está constantemente em execução ou a Velha Geração entra em um estado descontrolado. Em tal situação, é necessário equilibrar a frequência da Geração Jovem com o desempenho que exige o aumento do tamanho desta área coletora. As regiões Geração Permanente e Geração Antiga do coletor de lixo têm um impacto significativo no desempenho do aplicativo e no uso de memória. Esta grande operação de limpeza de lixo passa pela pilha para retirar objetos mortos. O processo leva mais tempo do que uma compilação secundária e o impacto no desempenho pode demorar mais. Quando a intensidade de depuração é alta e o tamanho da área da Antiga Geração é grande, o desempenho de todo o aplicativo fica prejudicado devido aos eventos "Parar o mundo". A otimização da coleta de lixo requer o monitoramento da frequência com que um programa é executado, o impacto no desempenho geral e como ajustar as configurações do aplicativo para reduzir a frequência do monitoramento. Talvez seja necessário identificar o mesmo objeto colocado mais de uma vez sem que o aplicativo tenha que se isolar do posicionamento, ou talvez seja necessário encontrar os pontos de compressão que estão impedindo todo o sistema. Para obter o equilíbrio certo, é necessário prestar muita atenção a tudo, desde a carga da CPU até os ciclos do coletor de lixo, especialmente se a geração mais jovem e a mais velha estiverem desequilibradas. Resolver vazamentos de memória e otimizar a coleta de lixo ajuda a melhorar o desempenho de um aplicativo Java. Você está literalmente fazendo malabarismos com muitas partes móveis. Mas com a abordagem correta de solução de problemas e ferramentas de análise projetadas para fornecer visibilidade rigorosa, você alcançará a luz no fim do túnel. Caso contrário, você sofrerá problemas relacionados ao desempenho. O posicionamento e o monitoramento cuidadosos da memória desempenham um papel crítico em um aplicativo Java. Você precisa assumir o controle total da interação entre coleta de lixo, descarte de objetos e desempenho para otimizar seu aplicativo e evitar erros de falta de memória. As ferramentas de monitoramento ajudam você a ficar por dentro de possíveis problemas e destacar tendências de utilização de memória para que você adote uma abordagem proativa na solução de problemas. Vazamentos de memória geralmente mostram a ineficácia da solução de problemas da maneira usual, especialmente se você encontrar valores de parâmetros de configuração incorretos, mas resolver problemas relacionados à memória pode ajudá-lo a evitar rapidamente incidentes que atrapalhem. A perfeição do ajuste de memória Java e GC torna seu processo de desenvolvimento muito mais fácil.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION