Extrair informações úteis dos seus arquivos de log é fundamental para o sucesso do seu aplicativo Java. Os dados de registro lhe darão uma visão valiosa do desempenho, estabilidade e usabilidade de sua aplicação.
Analizadores de dados de registro podem parecer tediosos, mas não precisam ser. Há uma variedade de ferramentas para ler, analisar e consolidar dados de log. Ferramentas básicas de linha de comando como grep, uniq, e sort podem combinar e extrair informações úteis de arquivos de log. Analisadores de logs mais avançados como Logstash ou Fluentd podem extrair dados chave dos seus logs em fichas facilmente pesquisáveis. Serviços de registro baseados em nuvem como o SolarWinds® Loggly® armazenam seus dados de registro para você e oferecem recursos sofisticados de análise, eliminando a necessidade de manter registros você mesmo.
Esta seção explora alguns métodos e ferramentas para analisar registros com o objetivo de melhorar seus aplicativos.
- Finding the Most Common Exceptions
- Finding Exceptions by Type Using Grep
- Finding Exceptions by Class Using Grep
- Usando uma solução de gerenciamento de log
- Problemas de Depuração de Produção
- Reagrupar informações sobre o problema
- Identificar a causa do problema
- Resolver o problema e evitar que ele se repita
- Mais Ferramentas de Depuração de Produção
- jdb
- OverOps
- BTrace
- Chronon
- jhsdb
- Tracing Transactions
- Tracing Unique IDs
- Tracing Method Calls
- Métodos de Rastreamento em Log4j
- Métodos de rastreamento em SLF4J
- Managing Memory Usage
- Garbage Collection
- Memory Leaks
- Recursos adicionais
- Veja-o. Analise-a. Inspecione-o. Resolva-o
Finding the Most Common Exceptions
Finding the most common exceptions can help you pinpoint areas of poor performance in your Java application. A maioria das estruturas de registro registra o tipo de exceção, a mensagem de exceção e o método em que a exceção ocorreu. Usando Log4j, um log de exceções parecerá similar a um dos seguintes.
09:54:44.565 ERROR Log4jTest.MathClass - java.lang.ArithmeticException: / by zero10:00:10.157 ERROR Log4jTest.Log4jTest - java.io.FileNotFoundException: myFile (No such file or directory)10:00:10.157 ERROR Log4jTest.Log4jTest - java.io.FileNotFoundException: newFile (No such file or directory)
Se o seu output inclui traços de pilha, veja a seção Parsing Multiline Stack Traces deste guia.
Vamos começar com um exemplo simples usando a popular ferramenta GNU grep. Depois, vamos mostrar como uma ferramenta de gerenciamento de logs pode facilitar ainda mais.
Finding Exceptions by Type Using Grep
O seguinte comando Unix irá encontrar exceções, extrair o tipo de exceção e contar o número de ocorrências. grep é uma popular ferramenta de linha de comando que executa a correspondência de padrões, enquanto uniq e grupo de ordenação e ordenar os resultados, respectivamente:
$ grep -o "\w*Exception" myLog.log | sort -r | uniq -c
Neste exemplo, a expressão regular \w*Exception
corresponde a qualquer palavra que termine com “Exception”. A bandeira -o
diz ao grep para imprimir apenas as partes da saída que combinam com a string de pesquisa. sort -r
ordena o resultado em ordem inversa, enquanto uniq -c
agrupa os resultados e conta o número de ocorrências. Como resultado, acabamos com uma contagem de exceções pelo tipo.
2 FileNotFoundException1 ArithmeticException
Também podemos usar o grep para pesquisar o log para cada instância específica dessas exceções.
$ grep ArithmeticException myLog.log09:54:44.565 ERROR Log4jTest.Log4jTest - java.lang.ArithmeticException: / by zero
Isso retorna cada linha contendo uma instância da string de busca ArithmeticException
, com a própria string de busca destacada na saída.
Finding Exceptions by Class Using Grep
Você também pode procurar por exceções pela classe em que elas ocorreram. Para eventos de log de linha única, o seguinte comando agrupa o número de exceções por classe e por tipo. grep extrai o nome da classe procurando por uma palavra seguida por um caractere em particular (este exemplo usa um hífen, embora qualquer caractere possa ser usado desde que seja único para a entrada). Embora este caractere não seja essencial, ele nos ajuda a localizar o nome da classe no evento de log. Com o operador OR (denotado por |), o grep também extrairá o nome da exceção procurando por uma palavra contendo a string “Exception”.
sed é outro utilitário de linha de comando que pode ser usado para formatar a saída do grep. Neste exemplo, sed remove o caractere especial da nossa saída, bem como qualquer caractere de nova linha. O output é então canalizado para os comandos uniq e sort para respectivamente agrupar e ordenar os resultados.
$ grep -o "w* -|\w*Exception" myLog.log | sed 'N; s/ -n/ /' | sort -r | uniq -c
O resultado nos aproxima das principais áreas problemáticas:
2 Log4jTest FileNotFoundException1 MathClass ArithmeticException
Com estes resultados, podemos usar o grep para encontrar mais detalhes sobre os problemas que ocorrem em classes específicas. Por exemplo, o seguinte comando recupera exceções que ocorreram em MathClass
class.
$ grep -e "MathClass.*Exception" myLog.log09:54:44.565 ERROR Log4jtest.MathClass - java.lang.ArithmeticException: / by zero
Usando uma solução de gerenciamento de log
A maioria das soluções de gerenciamento de log oferece maneiras de agrupar e pesquisar entradas de log com base no tipo de log, conteúdo da mensagem, classe, método e thread. Se seus logs já estão sendo analisados e armazenados, muitas soluções podem graficar e ordenar exceções pelo número de ocorrências. Esta é uma operação de apontar e clicar em Loggly para que você não precise memorizar comandos de grep complicados. O Loggly também indexa os registros de log, fazendo pesquisas e contagens muito mais rápidas do que o grep. Por exemplo, podemos usar o explorador de campo para ver uma lista de exceções e sua freqüência, então clique em uma exceção para ver todos os logs relevantes.
Usando o Explorador de Campo Loggly para encontrar rapidamente os logs por tipo de exceção.
Loggly também oferece ferramentas de visualização que podem ser mais informativas do que a saída baseada em texto do grep. Gráficos como este podem ajudá-lo a priorizar recursos para sprints de desenvolvimento ou para patches após uma nova versão.
Exceções de gráficos por frequência em Loggly.
Problemas de Depuração de Produção
Quando se trata de problemas de produção, o tempo é essencial. Um erro crítico em sua aplicação não deixará seus usuários infelizes; também diminuirá as vendas e reduzirá a confiança em sua aplicação ou serviço.
Na maioria dos casos, a resolução de um problema pode ser dividida em três passos-chave:
- Reagrupar informações sobre o problema
- Identificar a causa do problema
- Procurar uma solução e evitar que o problema se repita
Reagrupar informações sobre o problema
O primeiro passo é recolher informações sobre o problema. Reúna o máximo de informações possíveis – capturas de tela, relatórios de falhas, logs, links (para serviços web), etc. – para ajudar a reduzir as causas potenciais. Você vai querer que o usuário que experimentou o problema forneça informações detalhadas sobre o evento, incluindo: quando e onde no programa o problema ocorreu, suas ações que levaram ao problema, o ambiente operacional e qualquer comportamento estranho ou incomum do programa antes e depois que o problema ocorreu.
A partir daí, você pode começar a coletar informações de log. Se a sua aplicação é um serviço alojado, comece a recuperar os logs a partir da web e dos servidores da aplicação. Se a sua aplicação é um pacote de software distribuído, peça ao usuário para incluir dados de log em seu relatório de bug. Alternativamente, se a sua aplicação enviar eventos de log para um servidor de log centralizado, então seus logs estarão imediatamente disponíveis.
Após ter uma quantidade razoável de dados sobre o problema, você pode começar a rastreá-lo no código.
Identificar a causa do problema
Após coletar informações sobre o problema, o próximo passo é identificar sua causa. Reproduzir um bug em um ambiente de desenvolvimento é uma das formas mais fáceis de validar sua existência, mas pode ser demorado e pode não funcionar em todos os casos. Ter um conjunto completo de logs lhe trará diretamente à fonte do problema, economizando tempo e frustração.
Um relatório de bug lhe dará uma idéia geral do que é o problema e onde ele ocorreu. Usando sua ferramenta de gerenciamento de logs de escolha, você pode restringir sua busca a uma gama menor de entradas de logs, procurando por um token de dados único, como um nome de usuário, ID de sessão ou texto de mensagem.
Vamos rodar em um cenário de exemplo para demonstrar como depurar um sistema. Imagine que nós temos uma interface baseada na web para fazer login remoto em um website. A página web tem uma tela de login que executa autenticação básica usando um nome de usuário e senha. Os usuários têm relatado que não podem fazer login no site. A página web aceita sua entrada mas depois falha com um erro genérico.
Um exemplo de website relatando um erro após uma tentativa de login.
Esta mensagem não nos dá muitas informações além de uma indicação genérica da severidade do log. Pesquisar em um gerenciador de logs por entradas com “Severe” no nível ou mensagem pode resultar em centenas de resultados sem nenhuma garantia de que algum deles esteja relacionado com o problema em questão. Felizmente, nosso Logger
também registrou o nome de usuário do usuário que sofreu o erro, então podemos filtrar no nome de usuário “admin”.
Ver uma exceção Java em Loggly.
Se pesquisarmos, vemos que a causa do erro é uma tabela ausente ou inválida. Este é um problema sério, pois pode indicar uma exclusão acidental ou corrupção de banco de dados.
ERROR: Exception for user admincom.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'test_schema.users' doesn't exist...com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1651) at TestApplication.Test.doPost(Test.java:33) at javax.servlet.http.HttpServlet.service(HttpServlet.java:646) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at...
Localizando através do rastreamento da pilha, vemos o serviço falhar na linha 33 do Test.java. Esta linha consiste numa instrução SQL que retira dados sobre um utilizador da tabela test_schema.users.
rs = stmt.executeQuery("SELECT * FROM test_schema.users WHERE username = " + username + " AND Password = " + password);
Quando tentamos executar esta consulta num front end SQL, encontramos que a tabela “test_schema.users” não existe. No entanto, a base de dados tem uma tabela “test_schema.user”. Em algum momento, um desenvolvedor pode ter digitado erroneamente o nome errado da tabela e empurrado o formulário revisado para a produção. Agora sabemos qual é o problema e onde ele aparece em nosso código.
Usando SolarWinds Loggly, você pode depurar e encontrar a causa raiz dos problemas rastreando erros através de sua pilha de aplicações, através de múltiplos eventos de log, e até mesmo apontando a linha de código relevante no GitHub.
Resolver o problema e evitar que ele se repita
Agora que identificamos o problema e sua causa, o passo final é corrigi-lo. O nosso exemplo de login foi um caso exagerado com uma solução relativamente fácil, mas você pode encontrar bugs mais complicados enraizados em diferentes áreas da sua aplicação. Antes de saltar para a correção rápida e suja, considere cuidadosamente como a sua alteração terá impacto na aplicação. Esta é realmente a melhor solução para o problema? É possível que a sua alteração interfira com outro componente? Esta correção dificultará a introdução de novas correções ou características mais adiante? Esta correção também evitará que problemas semelhantes sejam criados mais tarde?
Por exemplo, e se dois usuários diferentes conseguissem criar duas contas separadas com o mesmo nome de usuário e senha? Você poderia impor um nome de usuário único para todos os usuários, mas como isso afetaria sua estrutura de banco de dados, seu código de autenticação e sua base de usuários existente? Você poderia adicionar um novo campo único como um endereço de e-mail e torná-lo obrigatório, mas e se alguns dos seus usuários atuais não tiverem fornecido endereços de e-mail? E se alguns endereços de e-mail forem compartilhados entre várias contas? Como essas novas regras afetarão suas páginas de registro, pesquisa e administração?
Você também vai querer considerar o atraso entre criar uma correção e implementá-la. Em um ambiente empresarial, seu código precisará ser revisto por outros desenvolvedores, integrado à base de código, construído, testado por QA, encenado, e talvez passar por vários outros passos antes de chegar à produção. Sua empresa pode ter protocolos específicos para bugs críticos ou sensíveis ao tempo que você precisa seguir. Depois de encontrar uma solução, inclua a sua resolução na sua documentação, bem como quaisquer soluções razoáveis para o problema. Desta forma, seus clientes ainda podem usar seu produto, e sua equipe de suporte pode reduzir o número de relatórios de bugs duplicados enquanto a correção faz seu caminho para a produção.
Após a implementação da correção, continue monitorando sua aplicação de produção para verificar se o problema foi resolvido. Por exemplo, após implantar a correção para o problema do banco de dados no exemplo acima, não devemos mais ver eventos com “Tabela ‘test_schema.users’ doesn’t exist”. Se os erros continuarem ou se começarmos a ver novos erros, sabemos que a correção não funcionou. Idealmente, veremos um padrão como o mostrado na seguinte captura de tela, onde o número de erros cai para zero imediatamente após a implantação do patch.
Charting the number of logs containing an error in Loggly.
Mais Ferramentas de Depuração de Produção
Embora o log seja o método experimentado e verdadeiro de resolução de problemas e depuração de aplicativos, essas ferramentas podem ajudá-lo a obter mais informações sobre como seu aplicativo funciona.
jdb
jdb, o Java Debugger, é um utilitário de linha de comando para depuração de classes Java. jdb é fácil de usar e vem empacotado com o Java Development Kit. Usando o jdb cria uma nova máquina virtual Java (JVM), permitindo a depuração de uma classe sem afetar nenhum programa em execução. Você também pode usar o jdb para depurar aplicações em execução, adicionando os seguintes parâmetros ao seu comando java:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n
Quando o JVM inicia, ele atribui um número de porta para conexões jdb de entrada. Você pode então anexar à instância JVM em execução usando jdb -attach:
$ jdb -attach
Você pode usar isto, por exemplo, para conectar seu depurador a uma instância em produção em execução. Tenha cuidado ao usar pontos de interrupção neste cenário, já que ele pode pausar uma thread ativa. Isto pode ter consequências se, por exemplo, um cliente estiver usando a aplicação enquanto você estiver depurando-a. Para mais informações, veja a documentação Java em jdb.
OverOps
OverOps é um conjunto de ferramentas para monitorar aplicações, analisar código e detectar problemas. Ao contrário do software de registo, que se baseia na saída gerada por uma aplicação em execução, o OverOps liga-se directamente à Máquina Virtual Java para mapear a base de código da aplicação, ler variáveis e registar erros. Isso permite que ele capture mais erros e registre mais dados do que até mesmo o framework de registro da aplicação. OverOps usa um modelo de software como serviço (SaaS), onde as métricas são coletadas e armazenadas nos servidores de nuvem do OverOps. No entanto, você também pode implementá-lo no local. Em ambos os casos, você pode visualizar seus dados usando uma interface baseada na web.
BTrace
BTrace é uma ferramenta de rastreamento que permite monitorar todos os aspectos de sua aplicação, desde nomes de classes até erros. BTrace utiliza uma abordagem de programação orientada a um aspecto que envolve o uso de anotações, que especificam onde e como BTrace monitora sua aplicação. Por exemplo, o seguinte BTrace monitora e registra cada chamada para o pacote javax.swing
import com.sun.btrace.annotations.*;import static com.sun.btrace.BTraceUtils.*; @BTrace public class AllMethods { @OnMethod( clazz="/javax\.swing\..*/", method="/.*/" ) public static void m(@ProbeClassName String probeClass, @ProbeMethodName String probeMethod) { print(Strings.strcat("entered ", probeClass)); println(Strings.strcat(".", probeMethod)); }}
Para mais informações sobre o BTrace, veja o repositório BTrace GitHub e Wiki.
Chronon
Chronon permite que você rebobine e reproduza todo o fluxo de execução de uma aplicação. Ele registra cada alteração individual feita durante a vida de uma aplicação, permitindo que você reproduza o estado da aplicação a qualquer momento. As gravações são salvas em arquivo, facilitando a transferência do histórico de execução de uma máquina de produção para uma máquina de desenvolvimento para testes.
Chrononon consiste no Chronon Recording Server, que permite gravar aplicações Java remotamente; o Chrononon Embedded, que incorpora o gravador dentro de uma aplicação; e o Time Travelling Debugger, que permite reproduzir novamente as gravações.
jhsdb
jhsdb (Java HotSpot Debugger) é um conjunto de ferramentas para depuração, análise e criação do perfil do JVM padrão fornecido com o OpenJDK e o Oracle JDK. jhsdb permite que você se apegue aos processos Java em execução, tire instantâneos de traços de pilha e até mesmo analise JVMs com falhas. Você pode usá-lo para acessar a pilha, cache de código, estatísticas de coleta de lixo, e muito mais. Para saber mais, veja a página de documentação jhsdb.
Tracing Transactions
Quando ocorre um problema, é importante saber onde o problema começou e como ele afetou o resto da sua aplicação. Isso já é difícil o suficiente em uma aplicação monolítica, mas se torna ainda mais difícil em uma arquitetura orientada a serviços distribuídos, onde uma única solicitação pode atingir dezenas de serviços. Nem sempre é óbvio qual serviço contém a causa raiz do erro, ou como ele afetou outros serviços. O rastreamento fornece os dados necessários para seguir o caminho de execução da sua aplicação e detalhar a causa exata de um problema.
Na seção de Problemas de Produção de Depuração do guia, nós caminhamos por um exemplo onde um usuário estava tendo dificuldade para entrar em uma aplicação web devido a uma tabela de banco de dados inválida. Nesta seção, vamos mostrar como o rastreamento de transações teve um papel central na resolução do problema.
Tracing Unique IDs
Para rastrear uma seqüência de eventos de log, você precisa de alguma forma de identificar de forma única os logs relacionados. Um ambiente multiusuário poderia gerar centenas de logs idênticos, tornando difícil a pesquisa baseada em timestamp ou Logger
. Uma solução mais fácil é incluir um identificador único com entradas de log relacionadas. Este identificador poderia ser um nome de usuário, um ID de sessão, uma chave API ou um Identificador Universalmente Único (UUID). Felizmente, o ThreadContext é perfeitamente adequado para este trabalho.
No exemplo de problemas de produção de depuração, nós tínhamos uma interface de usuário baseada na web que estava hospedada em um servlet Tomcat, que se conectava a uma base de dados MySQL. Os usuários inseriram suas credenciais na página web, e após pressionar enviar, o servlet executou uma consulta comparando suas credenciais com aquelas armazenadas no banco de dados. Se o usuário foi autenticado com sucesso, então eles foram redirecionados para a página principal. Se um erro ocorresse, detalhes sobre o erro eram registrados e os usuários eram apresentados com uma mensagem genérica.
Nós fomos capazes de depurar este problema, incluindo os nomes de usuário dos usuários na mensagem de registro. Isto permitiu-nos procurar rapidamente por eventos de registo relacionados com o utilizador admin. Construindo este mesmo exemplo, nós podemos usar ThreadContext.put()
para mapear um nome de usuário para um Logger
. Quando os usuários submetem suas credenciais, o servlet insere o método doPost()
, que adiciona os nomes de usuário dos usuários ao ThreadContext:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {ThreadContext.put("username", request.getParameter("username"));logger.info("Entering doPost().");...}
Usando Log4j com o padrão %p : %m%n
resulta na seguinte entrada:
INFO : Entering doPost().
Podemos adicionar eventos similares para acessar o banco de dados MySQL, deixando o método doPost, e executando outras ações. Desta forma, se o usuário acionar uma exceção, nós sabemos exatamente o que o usuário estava fazendo quando a exceção aconteceu.
Tracing Method Calls
Muitos frameworks de log fornecem métodos nativos para traçar o caminho de execução de uma aplicação. Estes métodos variam ligeiramente entre frameworks, mas seguem o mesmo formato geral.
-
traceEntry()
marca o início de um método. -
traceExit()
marca o fim de um método. Para métodos que retornam um objeto, você pode simultaneamente retornar o objeto e registrar o evento comreturn logger.exit(object)
. -
throwing()
marca uma exceção improvável de ser tratada, tal como umRuntimeException
. -
catching()
marca uma exceção que não vai ser re-destruída.
Você pode encontrar informações específicas sobre estes métodos na documentação do Logger para Log4j, Logback, e java.util.logging
.
Métodos de Rastreamento em Log4j
Como um exemplo usando Log4j, nós substituiremos os métodos Logger.info() em nosso servlet por métodos de rastreamento.
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { logger.entry(); ... logger.exit();}
Também vamos alterar o Appender's PatternLayout
para mostrar o nome da classe, método e número de linha (o %class
, %M
, e %line
padrões de conversão).
<PatternLayout pattern="%p %class %M %line: %m%n" />
Métodos de rastreamento de eventos de log no nível TRACE
, o que significa que teremos que alterar o nível Logger's
de DEBUG
para TRACE
. Caso contrário, as mensagens de log serão suprimidas.
<Loggers> <Root level="trace"> <AppenderRef ref="Console"/> </Root></Loggers> TRACE DatabaseApplication.Login doPost 26: entry...TRACE DatabaseApplication.Login doPost 59: exit
Métodos de rastreamento também fornecem os seus próprios Markers
. Por exemplo, os métodos Logger.entry()
e Logger.exit()
exibem ENTER
e EXIT
respectivamente.
ENTER 14:47:41.074 TRACE DatabaseApplication.Login - EnterEXIT 14:47:41.251 TRACE DatabaseApplication.Login - Exit
Métodos de rastreamento em SLF4J
SLF4J usuários podem tirar vantagem do MDC para rastrear eventos de log. Similar ao Log4j, valores MDC podem ser usados com um Appender usando o padrão de conversão %X.
SLF4J também fornece métodos entry()
, exit()
, throwing()
, e catching()
através da classe XLogger
(Extended Logger). Você pode criar uma instância XLogger
usando XLoggerFactory.getXLogger()
.
package DatabaseApplication;import org.slf4j.Logger;import org.slf4j.LoggerFactory; import org.slf4j.ext.XLogger;import org.slf4j.ext.XLoggerFactory; public class Login extends HTTPServlet { final static XLogger xlogger = XLoggerFactory.getXLogger(Login.class.getName()); final static Logger logger = LoggerFactory.getLogger(Login.class.getName()); @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { xlogger.entry(); .. xlogger.exit(); }}
Adicionar os padrões de conversão %classe e %line para logback.xml.
<configuration> <appender name="Console" class="ch.qos.Logback.core.ConsoleAppender"> <encoder> <pattern>%-5level %class{36} %M %L: %msg%xEx%n</pattern> </encoder> </appender> <root level="trace"> ... </root></configuration>
As entradas de log resultantes são idênticas às criadas pelo Log4j.
Managing Memory Usage
O gerenciamento de memória é frequentemente negligenciado em linguagens de nível superior como o Java. Enquanto a quantidade média de memória nos dispositivos modernos está aumentando, o uso de alta memória pode ter um grande impacto na estabilidade e desempenho da sua aplicação. Uma vez que a Máquina Virtual Java não pode mais alocar memória do sistema operacional, seu programa pode terminar e travar. Saber como gerenciar a memória irá evitar problemas à medida que sua aplicação cresce em tamanho.
Por exemplo, imagine que quiséssemos armazenar um conjunto de números começando por um. Um usuário fornece um limite superior e o programa armazena cada número inteiro de “1” até esse limite. Vamos usar um loop while com um contador para adicionar cada número a um array.
import java.util.Scanner;...System.out.print("Please enter the maximum size of the array: ");Scanner scanner = new Scanner(System.in);int limit = scanner.nextInt(); ArrayList intArray = new ArrayList(); int count = 1;while (count <= limit) { intArray.add(count);}
Você deve ter notado que count
não incrementa no loop. Este é um grande problema; se o usuário inserir qualquer número maior que zero, o array continuará a crescer até que a JVM use toda sua memória disponível e o programa trave.
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:2245)at java.util.Arrays.copyOf(Arrays.java:2219)at java.util.ArrayList.grow(ArrayList.java:242)at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)at java.util.ArrayList.add(ArrayList.java:440)at MemoryTest.main(MemoryTest.java:10)
Garbage Collection
Para reduzir o uso de memória, a Máquina Virtual Java realiza um processo de limpeza periódica conhecido como garbage collection. A coleta de lixo busca blocos de memória que não estão mais em uso e os torna disponíveis para reutilização. Os recursos a seguir explicam com mais detalhes o processo de coleta de lixo do Java.
- Noções básicas de coleta de lixo Java (Oracle)
- Guia de ajuste da coleta de lixo da máquina virtual HotSpot (Oracle)
- Entendendo Logs G1 GC (Poonam Bajaj)
- Guia de ajuste da coleta de lixo (Atlassian)
O coletor de lixo gera informações de diagnóstico que podem ser úteis para aplicações de perfil de desempenho. Você pode registrar essas informações passando -Xlog:gc para a JVM ao iniciar sua aplicação. Após cada execução, o coletor de lixo imprime estatísticas sobre o processo de coleta de lixo no formato Unified JVM Logging. Aqui está um exemplo.
Using G1 Periodic GC disabled GC(0) Pause Young (Normal) (G1 Evacuation Pause) 7M->1M(64M) 8.450ms
Using G1
diz qual método de coleta de lixo está sendo usado. O coletor Garbage-First (G1) é frequentemente habilitado por padrão em computadores multiprocessados com grandes quantidades de RAM. Periodic GC disabled
indica que o processo de coleta de lixo não vai se repetir. A terceira linha nos diz que isto é uma Pausa de Evacuação, onde os objetos são copiados entre regiões de memória com base em se eles ainda estão em uso. Pause Young
diz-nos que o processo limpou a geração jovem, que é onde os novos objectos são alocados. Como resultado, o uso total de memória pelos objetos da geração jovem caiu de 7M para 1M dos 64M alocados, e o processo levou 8.450ms
.
Com o coletor de lixo imprimindo informações de diagnóstico para o console, podemos ver como a quantidade de memória alocada (o tamanho da pilha) continua a crescer ao longo do tempo. Vamos remover os tempos de CPU para simplificar.
$ java -Xlog:gc MemoryLeakDemo Using G1 Periodic GC disabled GC(0) Pause Young (Concurrent Start) (G1 Humongous Allocation) 25M->23M(64M) 4.372ms GC(1) Concurrent Cycle GC(1) Pause Remark 50M->34M(64M) 0.250ms GC(1) Pause Cleanup 94M->94M(124M) 0.057ms GC(1) Concurrent Cycle 169.695ms GC(2) Pause Young (Concurrent Start) (G1 Humongous Allocation) 94M->94M(124M) 3.738ms GC(3) Concurrent Cycle...
Chart mostrando um aumento gradual no uso da pilha de JVM ao longo do tempo.
Pode adicionar carimbos de tempo a cada entrada com -XX:+PrintGCDateStamps
e -XX:+PrintGCTimeStamps
. Você também pode registrar a saída do coletor de lixo em um arquivo passando por Xlog:gc:file
: para a JVM ao iniciar sua aplicação.
Se você monitorar seus arquivos de log com o rsyslog, você pode então encaminhar os logs para um sistema de registro centralizado onde eles estarão analisados e prontos para análise em tempo quase real. Aqui está o aspecto de um gráfico de linha de tempo em Loggly. Ele mostra o tamanho total da pilha, assim como o tamanho da pilha antes e depois da execução do coletor de lixo.
Memory Leaks
Um vazamento de memória é uma situação em que um programa aloca memória mais rápido do que ele pode liberar. A maneira mais fácil de detectar um vazamento de memória é quando seu programa fica sem resposta, torna-se instável, ou causa OutOfMemoryErrors
. Em Java, você encontrará mais instâncias de Full GC
coletas conforme o uso de memória aumenta.
Você pode aprender mais sobre a saída do coletor de lixo e identificar vazamentos de memória no capítulo Resolução de problemas de vazamentos de memória da documentação Java.
Recursos adicionais
Cortar o tempo de depuração sem escrever uma única linha de código (Loggly)-Guia para usar Takipi
O Guia Final: 5 Métodos de Depuração de Servidores de Produção em Escala (Alta Escalabilidade)- Ferramentas e técnicas para depuração de problemas de produção
Gerenciamento de Memórias
Guia de Ajuste da Coleta de Lixo (Oracle)-Guia para entender e ajustar a coleta de lixo Java
Atualizar a Coleta de Lixo Java (CUBRID Blog)-Guia para a coleta de lixo Java
Veja-o. Analise-a. Inspecione-o. Resolva-o
Veja o que importa.
INICIAR TESTE GRATUITO