O Garbage Collection é uma ferramenta acoplada a JVM que realiza a limpeza de objetos em memória que não tem mais nenhuma utilidade. Sem esta ferramenta nossa aplicação sofreria constantemente de falta de memória ou mais conhecido por “memory leak”.

Para quem veio da linguagem C, pode estar acostumado a liberar manualmente objetos em memória através do comando “free”, assim podemos dizer especificamente qual objeto queremos liberar e exatamente em determinado instante. Mas em Java o cenário é outro, pois não é possível liberar explicitamente objetos em memória, o responsável por fazer isso é o Garbage Collection, e você (como desenvolvedor) não tem controle algum sobre ele.

Na verdade você pode apenas sugerir a ele que faça a limpeza de determinado objetos, mas não é garantido que o mesmo será feito, por isso, não desenvolva uma aplicação que dependa fortemente da limpeza do Garbage Collection, pois você estará cometendo um grande erro.

Já sabemos então que diferente da linguagem C (onde você libera os objetos em memória através da palavra reservada “free”), a linguagem Java não nos permite liberar objetos em memória de forma explícita, para isso existe o Garba Collection. Vamos agora entender como funciona essa ferramenta e qual as vantagens em ter essa “extra” oferecido pela JVM.

Funcionamento do Garbage Collection

Quando você usa a palavra reservada “new” em Java, o que você está fazendo é criando um objeto em memória e um ponteiro que irá apontar para esse objeto. Nada melhor do que um exemplo para ilustrar isso, nosso exemplo da listagem 1 já vem todo comentado para exemplificar a função de cada item na nossa classe Student.


class Student{
int a;
int b;
 
  /*
   * Atribute novos valores para os atributes
   * a e b do nosso objeto Student
   * */
  public void setData(int newA,int newB){
    a=newA;
    b=newB;
  }
  
  /*
   * Mostra os valores de a e b do nosso objeto
   * Student
   * */
  public void showData(){
    System.out.println("Value of a = "+a);
    System.out.println("Value of b = "+b);
  }
  
  
  public static void main(String args[]){
	/*
	 * Criamos um objeto Student na memória e um ponteiro chamado
	 * s1 que vai apontar para esse objeto.
	 * */
    Student s1 = new Student();
    
    /*
	 * Criamos um objeto Student na memória e um ponteiro chamado
	 * s2 que vai apontar para esse objeto.
	 * */
    Student s2 = new Student();
    
    /*
     * Até aqui nossos objetos referenciados por s1 e s2 não tem valor algum
     * nos atributos a e b, então nas linhas setData iremos colocar valores para
     * esses objetos em memória
     * */    
    s1.setData(1,2);
    s2.setData(3,4);
    
    /*
     * Mostramos os valores setados nos objetos s1 e s2 ao usuário
     * */
    s1.showData();
    s2.showData();
  }
}
Listagem 1. Criando objetos e ponteiros em memória

A JVM armazena nossos objetos na memória HEAP, então vamos ver como encontra-se nossa memória HEAP após a execução do código acima.

Memória HEAP após execução da listagem 1
Figura 1. Memória HEAP após execução da listagem 1

Perceba o seguinte: O S1 e o S2 são apenas ponteiros para nossos objetos criados pela palavra reservada “new”, eles não são o objeto em si. Esse conceito em muito se confunde entre os profissionais da área, que confundem o ponteiro com o objeto, e isso é muito importante para entendermos o funcionamento do GC (Garbage Collection).

O nosso primeiro objeto (do lado direito, referenciado por s1) tem valores de 1 e 2 para a e b respectivamente, enquanto que o nosso segundo objeto (referenciado por s2) tem valores de 3 e 4 para a e b respectivamente. Acontece que no momento atual nenhum dos objetos acima é elegível para serem limpos pelo GB, pois todos tem referência em memória, um com s1 e outro com s2.

Vamos agora adicionar algumas linhas a nosso código para criar 2 referências para um mesmo objeto.


class Student{
int a;
int b;
 
  /*
   * Atribute novos valores para os atributes
   * a e b do nosso objeto Student
   * */
  public void setData(int newA,int newB){
    a=newA;
    b=newB;
  }
  
  /*
   * Mostra os valores de a e b do nosso objeto
   * Student
   * */
  public void showData(){
    System.out.println("Value of a = "+a);
    System.out.println("Value of b = "+b);
  }
  
  
  public static void main(String args[]){
	/*
	 * Criamos um objeto Student na memória e um ponteiro chamado
	 * s1 que vai apontar para esse objeto.
	 * */
    Student s1 = new Student();
    
    /*
	 * Criamos um objeto Student na memória e um ponteiro chamado
	 * s2 que vai apontar para esse objeto.
	 * */
    Student s2 = new Student();
    
    /*
     * Até aqui nossos objetos referenciados por s1 e s2 não tem valor algum
     * nos atributos a e b, então nas linhas setData iremos colocar valores para
     * esses objetos em memória
     * */    
    s1.setData(1,2);
    s2.setData(3,4);
    
    /*
     * Mostramos os valores setados nos objetos s1 e s2 ao usuário
     * */
    s1.showData();
    s2.showData();
    
    /*
     * Criamos uma nova referência para o mesmo objeto que o s2
     * está apontando
     * */
    Student s3;
    s3=s2;
    s3.showData();
  }
}
Listagem 2. Criando uma referência s3

Veja como ficou nossa memória abaixo.

Nova referência S3
Figura 2. Nova referência S3

Perceba que temos o mesmo objeto, mas referenciado por 2 ponteiros distintos. A partir daqui vamos começar a usar o GC e ilustrar nossos exemplos.

Mas antes disso, tenha em mente o seguinte conceito: Objetos elegíveis a serem deletados pelo GC, são aqueles que não tem mais nenhuma referência em memória, ou seja, não possuem nenhum ponteiro apontando para ele, sendo assim eles tornam-se objetos “perdidos” em memória, pois não temos como recuperá-lo, e os GC pode apagar eles a qualquer momento.

Tendo o conceito acima em mente, vamos então setar o valor de s2 para nulo e ver o que acontece na nossa memória HEAP.


class Student{
int a;
int b;
 
  /*
   * Atribute novos valores para os atributes
   * a e b do nosso objeto Student
   * */
  public void setData(int newA,int newB){
    a=newA;
    b=newB;
  }
  
  /*
   * Mostra os valores de a e b do nosso objeto
   * Student
   * */
  public void showData(){
    System.out.println("Value of a = "+a);
    System.out.println("Value of b = "+b);
  }
  
  
  public static void main(String args[]){
	/*
	 * Criamos um objeto Student na memória e um ponteiro chamado
	 * s1 que vai apontar para esse objeto.
	 * */
    Student s1 = new Student();
    
    /*
	 * Criamos um objeto Student na memória e um ponteiro chamado
	 * s2 que vai apontar para esse objeto.
	 * */
    Student s2 = new Student();
    
    /*
     * Até aqui nossos objetos referenciados por s1 e s2 não tem valor algum
     * nos atributos a e b, então nas linhas setData iremos colocar valores para
     * esses objetos em memória
     * */    
    s1.setData(1,2);
    s2.setData(3,4);
    
    /*
     * Mostramos os valores setados nos objetos s1 e s2 ao usuário
     * */
    s1.showData();
    s2.showData();
    
    /*
     * Criamos uma nova referência para o mesmo objeto que o s2
     * está apontando
     * */
    Student s3;
    s3=s2;
    s3.showData();
    
    /*
     * Setamos o valor do ponteiro s2 para nulo, 
     * assim ele não fará mais referência a nenum objeto
     * */
    s2=null;
    s3.showData();
  }
}
Listagem 3. Setando o valor de s2 para nulo

Já que s2 não aponta para um objeto, então o GC agora poderá deletar esse objeto da memória, correto ? Errado, pois o s3 ainda está apontando para nosso objeto. Veja a figura 3.

s2 apontando para nulo
Figura 3. s2 apontando para nulo

Ambos os objetos ainda tem referências em memória, então o GC não poderá deletá-los. Vamos então tirar a referência de s3 para nosso objeto.


class Student{
int a;
int b;
 
  /*
   * Atribute novos valores para os atributes
   * a e b do nosso objeto Student
   * */
  public void setData(int newA,int newB){
    a=newA;
    b=newB;
  }
  
  /*
   * Mostra os valores de a e b do nosso objeto
   * Student
   * */
  public void showData(){
    System.out.println("Value of a = "+a);
    System.out.println("Value of b = "+b);
  }
  
  
  public static void main(String args[]){
	/*
	 * Criamos um objeto Student na memória e um ponteiro chamado
	 * s1 que vai apontar para esse objeto.
	 * */
    Student s1 = new Student();
    
    /*
	 * Criamos um objeto Student na memória e um ponteiro chamado
	 * s2 que vai apontar para esse objeto.
	 * */
    Student s2 = new Student();
    
    /*
     * Até aqui nossos objetos referenciados por s1 e s2 não tem valor algum
     * nos atributos a e b, então nas linhas setData iremos colocar valores para
     * esses objetos em memória
     * */    
    s1.setData(1,2);
    s2.setData(3,4);
    
    /*
     * Mostramos os valores setados nos objetos s1 e s2 ao usuário
     * */
    s1.showData();
    s2.showData();
    
    /*
     * Criamos uma nova referência para o mesmo objeto que o s2
     * está apontando
     * */
    Student s3;
    s3=s2;
    s3.showData();
    
    /*
     * Setamos o valor do ponteiro s2 para nulo, 
     * assim ele não fará mais referência a nenum objeto
     * */
    s2=null;
    s3.showData();
    
    /*
     * Faremos um objeto perder a referência de s3, assim como ele não terá a referência
     * de s2 nem de s3, ficará impossível executar o método showData dele, por isso o 
     * código da linha 72 não será executado (s3.showData())
     * */
    s3=null;
    s3.showData();
  }
}
Listagem 3. Setando s3 para nulo

Enfim, temos o nosso diagrama, com 1 objeto elegível para ser deletado pelo GC.

Objeto sem referências
Figura 4. Objeto sem referências

Pronto, agora quando o GC for realizar a limpeza, esse objeto será removido, pois não possui nenhuma referência em memória. Podemos ainda pedir gentilmente ao GC para realizar uma limpeza através do comando “System.gc()”, mas como dissemos, não é garantido que ele executará naquele determinado momento, por uma série de motivos que fica transparente ao desenvolvedor.

Conclusão

Enfim, o objetivo principal deste tutorial foi mostrar como o GC trabalha limpando os objetos em memória, mas para isso é preciso entender como funcionam as referências as objetos em memória, seus pontos e valores, só assim poderíamos falar do trabalho do Garbage Collection.