Vazamento de memória em Java

October 25, 2008

Isso existe?!

Sim….

Uma das grandes coisas que Java trouxe para o mundo do desenvolvimento foi uma moderna JVM, e um Garbage Colector eficiente, que foi se aprimorando ao longo dos anos. Com isto, o desenvolvedor pode deixar de se preocupar com questões de alocação e liberação de memória, fontes de perdas de cabelos e noites de sono perdidas de muitos programadores C/C++.

Nestas linguagens, um vazamento de memória se caracteriza como sendo uma região do heap alocada pelo desenvolvedor em algum momento do ciclo de vida de um software, cujo ponteiro se perde. Assim sendo, fica impossível (ou pelo menos extremamente difícil) recuperar esta área de memória para uso futuro. O efeito imediato é que o consumo de memória vai aumentando com o tempo, até tornar o sistema inviável.

É fato que este tipo de situação não existe em Java, uma vez que em última instância, a JVM sempre terá uma referência para uma área de memória alocada pelo desenvolvedor, e cuidará de fazer a limpeza caso necessário. Mágica? Não, tecnologia. Explicando de forma beeeeeeeeeeem resumida, o GC mantém uma contagem de quantas referências criadas pelo desenvolvedor apontam para um determinado objeto. Quando este contador chega a zero, está na hora de limpar a memória, pois com certeza esta não será mais útil.

Fica claro então que áreas de memória nunca serão perdidas em Java.

Miguel, você bebeu? Como é que vazamentos podem ocorrer então?

Bom, quem nunca perde a referência é a JVM.  Mas é possível perder o controle das referências para um objeto. Como eu disse antes, o GC coleta objetos sem referência. Mas e se você esquecer que tem referências perdidas no código e não zerá-las? Aí o objeto nunca será removido, e podemos ter um vazamento de memória, segundo Java. Para apaziguar certos ânimos, passarei a chamar este problema de Travamento de memória.

A boa notícia é que é bem mais fácil consertar em Java, e isto só se torna um problema real em grandes projetos que criam muitos objetos ao longo do tempo. Se você desenvolve exclusivamente para Web usando servidores de aplicação como JBoss, provavelmente pode fechar este artigo e voltar às suas atividades cotidianas.

A má notícia é que como se criou o mito de que este tipo de situação é impossível, ninguém hoje em dia se preocupa mais com isso. Não que isto seja ruim por si só. O problema é que quando suspeitamos de que isso pode estar acontecendo, consertar pode ficar mais complexo.

Uma situação clássica que pode gerar travamentos é o uso de Observers/Observables. Suponha que você tenha um objeto A do tipo Observable, e que ao longo da vida do seu software, você crie constantemente novas intâncias de objetos do tipo B que se cadastram com observadores de A. Nesta arquitetura, A contém referências a todos as intâncias de B, de forma não explícita (ou seja, contém referências à interface Observer). Basta alguém esquecer de limpar a referência da lista de observers e pronto…memória travada!

Como resolver isso? Bem, tentar racionalizar o processo de criação e limpeza de objetos, centralizando estes processos em pontos bem controlados. O processo de “limpeza” de recursos é muito importante, sobretudo quando a árvore de objetos e referências for grande.

==========================

Nota do dia 26/10

Como bem lembrado pelo leitor Thiago, uma outra possível solução é o uso de WeakReferences em Java. Um objeto deste tipo funciona como uma referência normal em Java, mas não trava o GC: caso um objeto seja referenciado apenas por WeakReferences, ele será coletado normalmente. Vale a pena olhar este artigo:

http://www.ibm.com/developerworks/java/library/j-jtp11225/index.html

tags:
posted in Desenvolvimento by Miguel Galves

Follow comments via the RSS Feed | Leave a comment | Trackback URL

  • http://gsd.uwaterloo.ca/~ttonelli/ Thiago

    Oi,

    eu não concordo com a afirmação de que A contem referencias não explicitas a instâncias de B (o que voce quer dizer com “nao explicita”, que nao estah em um field?). Pra causar “travamento de memória”, a referencia tem que ser explicita, mesmo que seja em Collections (por exemplo, objetos do tipo B vão estar provavelmente em uma lista em um field de A).

    No caso, vale a pena lembrar que java suporta weak references, que nada mais são do que referencias que são ignoradas pelo GC quando ele estah “contando o numero de referencias a um certo objeto”, ou seja, se soh existirem weak references, o GC coleta o objeto mesmo assim (e isso pode ser testado pelo método Reference.get()).

    Este artigo trata um pouco mais desse assunto: http://www.ibm.com/developerworks/java/library/j-jtp11225/index.html

  • Miguel

    Thiago,

    talvez eu tenha me expressado mal. Quando eu digo “não explícito”, eu me referi ao fato e que não existe uma Collection de Bs, ou um campo do tipo B, o que as vezes pode mascarar ou dificultar a busca. Mas é óbvio que a referência está lá. Este lance do observer pode ser um problema em sistemas grandes, onde objetos se registram e desregistram de forma espalhada. Acredite, caçar estas coisas pode ser complicado (estou passando por este tipo de situação no meu projeto atualmente). Quanto ao WeakReference, de fato é uma possibilidade e foi bom você ter mencionado isto. Ela resolve o problema, mas sinceramente não sei se é uma boa usar este recursos de forma indiscriminada. Ainda acho que um pouco de cuidado e métodos adequados de limpeza são as melhores soluções.

    []s

  • http://www.via6.com/topico.php?tid=238817 Miguel via Rec6

    Log4Dev » Vazamento de memória em Java…

    É possível sim ter problemas de vazamentos de memória em sistemas desenvolvidos em Java. Veja como isso pode ocorrer e como é possível prevenir….

  • http://www.juliohm.com.br JulioHM

    Realmente, programadores Java tendem a complicar tanto o código fonte do sistema que fica fácil perder o controle de onde estão as coleções e as instâncias dos objetos. Caso típico de um sistema super-arquiteturado e mal implementado.

  • http://bpfurtado.livejournal.com/ Bruno

    É JulioHM, estes programadores Java, só sabem “super-arquiteturar-tudo-mal-implementado”, agora os outros “tipos” de programadores não… aliás memory leaks foram inventandos pela JVM praticamente, eram só modelos teóricos e filosóficos que existiam até então (hahaha).

  • http://gsd.uwaterloo.ca/~ttonelli/ Thiago

    @Miguel

    Bom, tem que ter uma collection de Bs em A, ou em alguma super classe de A. Mas eu entendo o seu ponto, eh um trampo verificar que o codigo esta usando o protocolo de forma correta. Existem algumas tentativas de verificar esses protocolos e, como isso eh um pouco complicado pra um comentario, eu vou tentar escrever mais sobre isso num post. Assim q sobrar um tempinho aqui….

    @Bruno

    Nao foi Java que inventou memory leaks, foi C#!!! Java soh copiou tudo de .NET, por isso que tambem tem memory leaks!! ;-)

  • http://bpfurtado.livejournal.com/ Bruno

    Thiago,

    Java copiou de .NET?!? Wow… com todo respeito Thiago, de que mundo você está chegando?

    -Java primeira versão: 1996 -.Net primeira versão: 2002

    Acho que é senso comum que .Net foi desenhado já considerando os prós e contras das decisões utilizadas na plataforma Java, acho que isto é bemmm senso comum já faz um bom tempo.

    E como os criadores da plataforma .Net não tinham 6 anos de código legado para manter compatibilidade, puderam corrigir e melhorar muitos dos pontos fracos da Plataforma (delegate para closures, annotations, etc).

    Mas .Net foi a cópia da plataforma JavaSE, praticamente o maior tributo que Java poderia receber. Um cópia com melhorias, pelo motivo já comentado acima.

    Sem nem entrar no mérito qual plataforma é melhor, acho que este não é o foco aqui.

  • Miguel

    @Bruno, onde está o seu senso de ironia???????? Ou será que você concorda com o fato que C# teria criado o memory leak?

  • http://bpfurtado.livejournal.com Bruno

    Miguel, I guess I lost my faith… pretty sad huh?

    Não conheço o Thiago e não seria a primeira vez que alguém comete erros colossais em algum blog da web.

    Thiago, se foi ironia peço desculpas, hoje em dia tá dificil saber o que é humor e o que é falta de informação.

    Agora francamente, quem acha que uma VM munida de um GC pode impossibilitar memory leaks… espero que seja alguém na graduação ainda ;-)

  • http://gsd.uwaterloo.ca/~ttonelli/ Thiago

    Hehehe, você veio com ironia primeiro, eu soh continuei na sua.. e ainda emendei uma carinha pra ter certeza que você iria entender :-)

    Eu acho q ninguém aqui acredita que GC vai impossibilitar memory leaks, acho que ninguém disse isso. O Miguel inclusive tah mostrando como você pode ter memory leaks (ou “travamento de memoria”) em Java e, apesar do JulioHM ali em cima estar alfinetando programadores Java (eu tenho certeza que também tem memory leaks em Python, Ruby, ou whatever que você imaginar), ele não disse que uma VM com GC vai curar o problema (soh “sistemas não-super-arquiteturados, não-mal-implementados”).

    Keep your faith, it’s not lost (yet)!

  • http://bpfurtado.livejournal.com/ Bruno

    Thiago,

    Eu tenho certeza que o Miguel não quis dizer que um GC impossibilita memory leaks, apenas quis deixar claro que qualquer desenvolvedor com alguns anos de experiência deveria saber isto obrigatoriamente, perdoados apenas os graduandos ou recém formados (e olhe lá, hehe) :)

    Quanto a ironia, de fato me escapou a figura de linguagem, mesmo com a aplicação de recursos visuais, sorry again.

    Só para constar, já que o post está tão didático: memory leaks são privilégio de qualquer linguagem (com a ajuda de uma biblioteca ou não) que possibilite a alocação dinâmica de memória, simple as that :)

blog comments powered by Disqus

Switch to our mobile site

 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org