Atribuindo null

Um post bem curto sobre uma prática que aparece comumente em códigos por aí:

public void metodoQueListaUsuarios() {
  List<Usuario> lista = dao.pegaListaDeUsuarios();
  for(Usuario u : lista) {
    System.out.println(u.getNome());
  }
  lista = null; // pra que isso?
}

Essa atribuição da lista para null, como muitos de vocês sabem, é totalmente desnecessária, pois ao fim do método o escopo da variável lista termina e ela morre automaticamente, já que essa é uma variável de pilha. Achar que isto está ajudando o Garbage Collector é um engano.

Porém tem alguns casos que isto é aplicável. O primeiro é se lista fosse um atributo, e não uma variável local ao método. Mas se você está atribuindo null a um atributo no fim de todo método, ele tem grande potencial de ser refatorado para varíaveis locais.

O segundo caso é quando você tem um objeto X alocado no heap e não vai usá-lo mais, porém você ainda tem trabalho a fazer no escopo da variável que referencia X. O Garbage Collector não poderá coletar X, nem mesmo marca-lo para coleta, enquanto a sua referência existir, e pode ser que ela continue existindo por muito tempo, como no código abaixo:

public void metodoQueListaUsuarios() {
  List<Usuario> lista = dao.pegaListaDeUsuarios();
  for(Usuario u : lista) {
    System.out.println(u.getNome());
  }
  lista = null;
  dao.fazOperacaoDemorada();  // lista ainda em uso?
}

Neste caso faz sentido tentar liberar a variável o mais rápido possível. Caso contrário, durante a nossa dao.fazOperacaoDemorada(), o objeto estará presente na memória e não poderá ser coletado (as vezes, em vez de atribuir null, podemos obter o mesmo efeito criando um novo escopo com {}). Claro que essa atribuição merece ser documentada. Essa operacaoDemorada poderia ser mais grave ainda caso a thread vá ser colocada em wait.

Dizem também, mas aí já está além do meu conhecimento, que a virtual machine hoje em dia consegue detectar essas variáveis que não tem mais uso em um determinado escopo, eliminando-a antes mesmo que o seu escopo feche.

Tags: ,

7 Comentários

  1. Rodrigo Urubatan 04/01/2007 at 04:48 #

    na verdade, no segundo caso também não seria necessário, pois podemos verificar até em tempo de debug, que logo depois do for, a variavel lista não é mais acessivel, ou seja, o compilador ja se livra dela automaticamente …

    outra coisa que poderia ser feita, é pelo menos neste caso, alterar o código para algo parecido com isto:

    public void metodoQueListaUsuarios() {
      for(Usuario u : dao.pegaListaDeUsuarios()) {

        System.out.println(u.getNome());
      }
      lista = null;
      dao.fazOperacaoDemorada();  // lista ainda em uso?

    }

    ou algo parecido, assim também não seria necessário o código extra … e pelo menos na minha opinião ficaria um pouco mais legivel, em algum momento eu descompilo os códigos a cima para comparar e ver o que o compilador realmente faz 😀

  2. Paulo Silveira 04/01/2007 at 05:12 #

    Urubatan, o debug pega, mas nao sei se o GC pega, como disse. Se alguem tiver o link para a otimizacao do JIT, ai eu fico convencido!

    E o codigo foi so um exemplo, claro que voce podia ter eliminado a variavel temporaria, mas imagine que nao pudesse. Ai voce tem duas solucoes, ou fazer o null, ou declara-la dentro de um outro escopo abrindo e fechando {} em volta do FOR e da declaracao

  3. Paulo Silveira 04/01/2007 at 05:18 #

    O que eu quis dizer com criar um novo escopo seria isso:

    public void metodoQueListaUsuarios() {
      {
        List<Usuario> lista = dao.pegaListaDeUsuarios();
        for(Usuario u : lista) {
          System.out.println(u.getNome());
        }
      }
      dao.fazOperacaoDemorada()
    }

    eliminando o lista = null.

  4. Sami Koivu 04/01/2007 at 13:16 #

    Muito legal o post, Paulo. Também vejo que muita gente parece estar um pouco confuso sobre essa atribuição de null e GC.

    No bytecode os escopos dos variáveis locais são definidos explicitamente definindo o começo do escopo e o tamanho do escopo num LocalVariableTable -atributo (ou LocalVariableTypeTable no caso dos generics).

    http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#5956

    Só que.. esse atributo não é requerido, acho que é só utilizado pelo debugger mesmo: “The LocalVariableTable attribute is an optional variable-length attribute of a Code (§4.7.3) attribute. It may be used by debuggers to determine the value of a given local variable during the execution of a method.”

    Então.. na verdade o compilador não ajuda a maquina virtual com essa questão de escopo. Pois na ausência de um LocalVariableTable o escopo definido no arquivo class seria o método inteiro. Isso em mente acho que a maquina virtual tem que determinar o escopo dos variáveis analisando o código. Isso parece estranho e portanto fiz um teste: Compilando com -g:none (instruindo o compilador a não incluir informação de debug)

    public void metodoQueListaUsuarios() {
    {
    List lista = dao.pegaListaDeUsuarios();
    for(Usuario u : lista) {
    System.out.println(u.getNome());
    }
    }
    dao.fazOperacaoDemorada();
    }

    e

    public void metodoQueListaUsuarios() {
    List lista = dao.pegaListaDeUsuarios();
    for(Usuario u : lista) {
    System.out.println(u.getNome());
    }
    dao.fazOperacaoDemorada();
    }

    produzem exatamente o mesmo código. Surpreendente, não?

  5. Sami Koivu 04/01/2007 at 13:20 #

    Desculpem a falta de formatação 🙂 E acrescentando que o teste e a teoria aplicam para Java 5, não tenho feito muita coisa com o Java 6 ainda.

  6. Hugo Vidal Teixeira 09/01/2007 at 06:24 #

    Esse post me lembrou dos tempos em que eu programava em C++/Delphi e tinha que desalocar possíveis objetos no final dos métodos (delete/free). Boa lembrança.

    O que eu gostaria de lembrar aqui é que se o método pegaListaDeUsuarios() retornar uma lista que já está pronta e é mantida como um “cache” dentro do DAO, então nenhuma tentativa conseguirá removê-la pelo GC (já que o DAO terá uma referência para ela). Essa idéia é, de fato, muito ruim, pois quebra o encapsulamento do DAO e permite que objetos estranhos alterem a lista sem restrições. Mas apenas gostaria de lembrar que isso pode (infelizmente) acontecer.

    Grande abraço,
    Hugo.

  7. Gladson Reis 06/06/2017 at 16:32 #

    Boa tarde !
    @SuppressWarnings(“unchecked”)
    public List findByNome(String nomeCidade) {
    return (List) createCriteria().add(Restrictions.like(“descricao”,nomeCidade)).addOrder(Order.asc(“uF”)) .addOrder(Order.asc(“descricao”)).list();
    }

    O Garbage Collector resolve tudo ?

Deixe uma resposta