Introdução ao Garbage Collector

em 28/11/2002 , por Guilherme Silveira
Introdução
Uma das vantagens de Java é que, ao reservar um espaço da memória para seu programa, você não precisa explicitar a liberaçao dele quando não utilizá-lo mais. Isto é, nada de free(ptr). Isto ajuda os programadores, uma vez que esse era o ponto de Memory Leak (Vazamento de Memoria) nas outras linguages: no momento que um objeto não desalocava a memória reservada após a saída do programa, aquele espaço ficaria reservado indefinidamente, sem uso. Saber quando devemos liberar um objeto da memória nem sempre é uma tarefa fácil. Vamos ver aqui porque você não precisa liberar essa memória em java. Este artigo também tira algumas duvidas sobre o metodo finalize() da classe java.lang.Object e do Garbage Collector que podem surgir tanto para programadores novatos e avancados. Apesar de ser uma introdução, é necessário já ter um conhecimento de java, especialmente sobre referências.
Como funciona
Toda vez que você faz uma referência a um objeto, a JVM (Java Virtual Machine) anota isto em um contador. Com isso, ele sabe quantas referências estão "apontando" para aquele pedaço da memória. Quando este contador atinge zero, este objeto é candidato (eligible) a ser varrido da memória! Em um momento oportuno, o JVM limpará este espaço de memória, dando lugar para novos objetos o utilizarem. Tenho falado aqui que é a JVM quem faz isso. Na verdade, é uma um recurso chamado Garbage Colletor (Lixeiro). Ela tem como responsabilidade alcoar memória, anotar quantas referências existem para cada objeto, e rodar uma thread que limpe os objetos que não tem referências. O Garbage Collector é nativo, isto é, varia de JVM para JVM. Tanto que algumas JVMs pagas oferecem políticas de varredura da memória, para poupar processamento e outros benefícios. Nota: Tipos primitivos alocados dentro de um método nunca são liberados desta maneira, já que eles ficam apenas no stack. (O Garbage Collector atua apenas na heap memory, e faz todo sentido).
Quando o Garbage Collector roda
Importante: A especificação da Java Virtual Machine não obriga o Garbage Collector a rodar nos momentos que você deseja. Isso significa que voce não pode obrigá-lo a rodar, mas pode dizer: "por favor, seu lixeiro, se possivel, rode":

Existem outras maneiras de efetuar esse pedido, mas nunca se esqueça que não existe maneira para obrigá-lo a rodar. O Lixeiro (Garbage Collector) é executado em duas etapas, primeiro ele procura os objetos que não estão sendo apontados (referenciados) em nenhum lugar. Por exemplo:

Após a segunda linha rodar, o objeto Guilherme vira lixo e está pronto para ser varrido. Quando, e se, o Lixeiro rodar, ele irá encontrar este objeto durante sua primeira fase. Na segunda etapa, ele pega esses objetos que não possuem mais referências, e rodara o método finalize() que possui a seguinte assinatura:

E por fim, ira liberar todas os objetos contidos dentro desse objeto que está sendo varrido (note que algum já pode ter sido varrido, mas este é um detalhe avançado). Neste momento o Lixeiro chega em sua segunda fase, onde verifica novamente se o objeto não possui nenhum ponteiro (ressuscitou) e procura novos objetos que ficaram sem ninguem utilizando eles. Dica: Nenhum objeto que está rodando um método será finalizado e varrido da memória, a não ser que ele seja uma Thread Daemon. O método finalize possui mil caracteristicas específicas dele. Por exemplo, não importa se durante o método uma exceçao for jogada, o objeto será varrido. Importante: Você pode chamar o metodo finalize a qualquer momento, porém o Lixeiro não toma nota que isso aconteceu, e ira chama-lo quando varrer o objeto da memória. Muito cuidado, pois esta prática não é recomendada. Aliás, algumas pessoas criticam o uso do finalize(). Outra peculiariedade do metodo finalize eh que ele nao eh chamado duas vezes pelo Lixeiro. Se o objeto ressuscitar, isto eh, durante seu metodo finalize ele criar um ponteiro de si mesmo em outro lugar qualquer (nao vale ponteiro em loop: ele apontando para si mesmo), seu metodo finalize nao sera chamado novamente e da proxima vez que o Lixeiro perceber que o objeto nao esta mais sendo utilizado, ira varrer o objeto da memoria SEM chamar o metodo finalize novamente. Dica: Eh importante lembrar que estamos falando de um metodo, e nao um construtor portanto, se o objeto possuir um codigo no metodo finalize, eh interessante colocar uma chamada para o mesmo metodo da classe pai, chamando ao mesmo tempo o finalize de sua classe pai.
Exemplo prático do funcionamento
Não se assuste com o exemplo! É importante você fazer o download do programa e rodá-lo, para entender o que está sendo limpado da memória. É muito recomendado que você faça mudanças no código para o melhor entendimento. O programa em anexo página traz quatro classes, sendo uma a classe aplicação, que roda o programa. A segunda classe, Pai possui um membro chamado Nome que corresponde ao nome da classe e um metodo finalize que mostra que a instancia esta sendo varrida. Depois vem a classe FilhoImortal que para programadores menos experientes parece ser imortal, uma vez que durante seu metodo finalize cria um link para ela mesma novamente. Durante o teste voce vera que o metodo finalize so sera chamado uma vez, fazendo com que o objeto seja realmente varrido. Por ultimo, a classe FilhoComChamada, temos uma chamada para super.finalize() que roda tambem o metodo de sua classe pai. A classe principal ira criar uma instancia de cada classe e jogar elas fora de uma maneira randomica, sempre mostrando quais sao os objetos que sobreviveram a uma chamada ao Lixeiro. Infelizmente nao temos como garantir que o funcionamento do programa vai ser o mesmo para todos, uma vez que o Lixeiro nao eh obrigado a rodar quando fazemos a chamada a System.gc(), porem o resultado pretendido eh que o metodo finalize seja chamado tres vezes (uma para cada objeto), uma vez a mais na classe Pai por causa da classe filha que chama super.finalize() e que quatro objetos sejam removidos da array que contem as instancias, uma vez que uma classe filha ira se ressuscitar (somente uma vez). Este arquivo java pode ser compilado e rodado:

Conclusão
Existe como você desabilitar o garbage collector, criar referências fantasmas para evitar que o GarbageCollector execute em momentos que você precisa de um alto desempenho e muitos outros recursos avançados que serão discutidos em um outro artigo. O domínio sobre o Garbage Collector deve ser a meta de um programador que deseja ter o total conhecimento do java.