Atribuindo null
Postado em 03. jan, 2007 por Paulo Silveira em Java
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.
ASSINE NOSSO RSS
Rodrigo Urubatan
04. jan, 2007
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
Paulo Silveira
04. jan, 2007
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
Paulo Silveira
04. jan, 2007
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.Sami Koivu
04. jan, 2007
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?
Sami Koivu
04. jan, 2007
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.
Hugo Vidal Teixeira
09. jan, 2007
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.