Resolver memory leaks

14 respostas
Hammer

seguinte, tenho algumas screenshots que tirei qd estava utilizando o JProfiler(q pena q so funciona 10 dias no trial)

e achei as possiveis razoes dos meus memory leaks

primeira ss:

podemos perceber que tem algumas classes q estao consumindo mt a memoria. um exemplo disso eh a classe date, que utilizo pra mostrar data e hora tambem na interface do sistema, alem de registrar a data no banco de dados de certos registros.

a maneira como estou utlizando eh a seguinte:

private static final DateFormat FORMATO = new SimpleDateFormat("HH:mm:ss");
        Calendar cal = new GregorianCalendar();

        String dia = new Integer(cal.get(Calendar.DAY_OF_MONTH)).toString();
        String mes = new Integer(cal.get(Calendar.MONTH)+1).toString();
        String ano = new Integer(cal.get(Calendar.YEAR)).toString();
        
        Timer t = new Timer("ClockTimer", true);
        t.schedule(new ClockTask(), 0, 1000);

private class ClockTask extends TimerTask {
        @Override
        public void run() {
            // Aqui chamamos o setHora através da EventQueue da AWT.
            // Conforme dito, isso garante Thread safety para o Swing.
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    // Só podemos chamar setHora diretamente dessa
                    // forma, pois esse Runnable é uma InnerClass não
                    // estática.
                    setHora(new Date());
                }
            });
        }
    }

estou fazendo algo de errado? pq ele esta ficando na memoria depois? eu faco com q o objeto cal aponte pra null antes de finalizar a janela

e o hashtable? pq esta consumindo tanta memoria? e o arraylist? eu uso o arraylist pra pegar algumas consultas no banco, mas depois aponot pra nulll tb pra qd o gc passar retirar ele da memoria.

ss 2:

o java.awt.eventqueueitem esta consumindo mais de 50mb de memoria, qual seria a causa disso?

ss 3:

podemos perceber mais uma vez q o clocktask esta consumindo mt memoria, pq será isso?
as minhas janelas q estoua brindo mesmo qd fecha o objeot fica na memoria, estou invocando da seguinte maneira:

java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new TabelaTaxas(1).setVisible(true); } });
estou fazendo algo de errado q esta resultando em ele nun sair da memoria?

14 Respostas

ViniGodoy

Você já começou pelo caminho certo, usando um profiler.

Já pensou em usar o profiler do Netbeans? Além de ser tão bom quanto esse, é de graça.

Primeiro de tudo, esse profiler mostra a quantidade de memória total alocada, ou só o que está residente no momento?
O netbeans suporta os dois modos. Se for o primeiro, vc precisará tirar 2 “screenshots” e compara-los.

Há várias coisas que são famosas por gerar memory leaks:

  1. Varíaveis static: Como elas nunca morrem e nem nunca saem de escopo, tudo que elas referenciam pode gerar memory leaks;
  2. Inner classes (em especial as que usam threads): Lembre-se as inner classes tem uma referência a uma classe que a contém. Então, se vc coloca-la dentro de um timer task (por exemplo), a classe não morrerá enquanto o timer morrer também. Da mesma forma, se vc usar um runnable como uma inner class para disparar uma thread, a classe que a contém não morrerá até que a thread pare.

Verifique se seu profiler tem a possibilidade de companhar o stack trace da alocação. Também verifique se é possível ver a lista de instâncias das classes que estão ocupando muita memória. Pegue algumas instâncias por amostragem e veja em que objetos a referência. Acompanhe isso até achar o objeto ligado ao gc. No caso do Netbeans, há um comando para fazer isso automaticamente (show next gc hook).

Hammer

bem, eu sou um pouco novo nesse negocio de profiler, vou tentar achar oq vc pediu, mas oq vc falou acha q meu encaixo no segundo caso principalmente

primeiro pq varias classes minhas q eu percebi q continuam na memoria tem como inner class ClockTask que extende a TimerTask.

mas eu nao crio um objeto e aponto pro clockTask, apenas dou um new sem ter um apontador.

mas percebo que meu objeto FORMATO da clase DateFormat eh static, q no caso se encaixaria no primeiro caso que vc falou

como poderia corrigir esses problemas?

e outra: eu abro mts JFrames utilizando o runnable, com poderia matar esse JFrame depois?

desde ja agradeço e muito a sua ajuda

ViniGodoy

O problema não é abrir um JFrame usando um Runnable. É o seu JFrame ter uma Runnable interno, que é usado dentro de uma thread separada.
Aí, só matando a thread. Não é o seu caso com o EventQueue.invokeLater.

Quando vc dá um new, você cria um novo objeto, independente se ele tem nome ou não. O apontador é diretamente o parâmetro de entrada do método schedule mas, dentro do método e da classe Task, ele é um apontador do mesmo jeito. Portanto, uma referência forte.

Para parar um timer após usa-lo, chame o método cancel(). Outra alternativa, já que você está usando o Swing, é usar o javax.swing.Timer ao invés do java.util.Timer. Ele ainda tem a vantagem de já disparar eventos na thread do Swing, poupando o tal EventQueue.invokeLater.

Lembre-se também de sempre de remover listeners após usa-los, especialmente os de classes que usam threads adicionais (tal como a classe Timer).

Hammer

bem, tentei dar um cancel, finalize e td mais, trokei pelo javax.swing.Timer mas minha classe q extende o jframe continua na memoria mesmo apos eu fechar ela.

oq posso fazer? isso esta gerando muitos problemas.

ViniGodoy

Baixe o netbeans. Aí posso ensinar a como usar o profiler dele para achar quem está referenciando esse JFrame.

Você fecha o frame com dispose() ou setVisible(false)?

Hammer

ja estou com o netbeans instalado, o 6.1, e estou fechando utilizando o dispose

Hammer

meu projeto esta no eclipse, tem como eu importar elepro netbeans?

ViniGodoy

Você precisa do Java 6 e da pasta JDK\bin no path.
Não precisa nem montar o projeto no Netbeans.

Seria uma boa colocar um botão na sua aplicação com a função “run gc”.

Então roda a tua aplicação.
Então, abre e fecha o tal frame.
E aperta o botão do “Run gc”.

Com a aplicação ainda aberta, vá até o prompt de comando e digite:
jps

Isso vai mostrar a sua aplicação associada a um número de processo.
Depois, digite no prompt:

jmap -dump:format=b,file=leak ####

Onde:
leak é um nome de arquivo (que ele vai criar)
e #### é o número de processo que vc obteve no jps.

O jmap vai criar um dump da sua memória, chamado leak. Esse dump pode ser analisado pelo netbeans. Observe que isso pode ser feito pelo pessoal de campo também, é muito prático, especialmente se vc usa outra IDE.

Ok, agora abra o netbeans. Em “Perfil” clique em “Carregar despejo de pilha”. Abra o arquivo leak.
Embaixo das abas com o nome dos arquivos, clique na opção “Classes”.

Ache o seu Frame que não sai da memória. Clique com o botão direito sobre ele e clique em “Exibir na visualização de instâncias”.

Vai abrir uma outra aba. Ela contém:
No lado esquerdo uma lista de todas as instâncias do seu JFrame ainda vivas (espero que 1 só).
No lado direito, no topo, tudo que essa instância referencia.
No lado direito, na parte de baixo, uma lista de quem referencia essa instância. Essa é a lista importante.

Clique com o botão direito sobre o primeiro elemento dessa lista e clique em “encontrar raiz GC mais próxima”. Isso vai achar quem é o link forte que aponta para essa instância. Agora, é só seguir a árvore, até encontrar uma classe que vc tenha controle e que aponte para a sua. Tem 99% de aí estar o seu problema.

Se precisar de ajuda, posta uns screenshots aí que eu te oriento mais.

Hammer

consegui importar para o netbeans

como faco pra descobrir para saber quem esta referenciando esse JFrame?

Hammer

vou fazer oq vc disse, jaja posto os resultados

Hammer

comando nao reconhecido esse jsp

minhas variavei de ambiente estao assim:

CLASSPATH
.;JAVA_HOME

JAVA_HOME
C:\Arquivos de programas\Java\jdk1.6.0_07

PATH
C:\Arquivos de programas\Java\jdk1.6.0_07\bin

ViniGodoy

Não é jsp é jps.

Hammer

bem, vou postar as fotos, e comforme vc falou,apontou para o clocktasks, testei em um q ainda n tinha trocado pelo timer do javax

mas, como memso usando o timer do javax ainda continua na memoria, deve ser o mesmo erro
ai esta as ss:

se eu der o comando para encontrar a raiz tb no clocktasks da nisso:

ViniGodoy

Abre a pasta do ClockTask e vê o que aponta para ela.
E daí veja o que aponta para o seu Timer.

Criado 25 de julho de 2008
Ultima resposta 29 de jul. de 2008
Respostas 14
Participantes 2