A configuração de objetos Java como nulos faz mais alguma coisa?

112

Eu estava navegando em alguns livros antigos e encontrei uma cópia de "Java Prático" de Peter Hagger. Na seção de desempenho, há uma recomendação para definir referências de objeto nullquando não forem mais necessárias.

Em Java, definir referências de objeto para nullmelhorar o desempenho ou a eficiência da coleta de lixo? Em caso afirmativo, em que casos isso é um problema? Classes de contêiner? Composição do objeto? Classes internas anônimas?

Eu vejo isso no código com bastante frequência. Este conselho de programação agora é obsoleto ou ainda é útil?

sal
fonte
2
Crie um perfil. Em tempos de execução modernos, você não deve ver nenhum aumento significativo no desempenho ou consumo de memória.
Jason Coco
12
@Jason, perfil dele? Isso pressupõe que traçarei o perfil de um conjunto de casos grande o suficiente para obter um conjunto de resultados bom o suficiente para responder a isso. E que eu não escolho um conjunto de casos em que o VM é otimizado o suficiente para mascarar os problemas de gc e desempenho. É por isso que estou perguntando isso aqui. Para ter uma ideia dos casos em que isso é um problema.
sal
1
Duplicado de stackoverflow.com/questions/449409/… .
Michael Myers

Respostas:

73

Depende um pouco de quando você estava pensando em anular a referência.

Se você tiver uma cadeia de objetos A-> B-> C, uma vez que A não seja alcançável, A, B e C serão todos elegíveis para a coleta de lixo (assumindo que nada mais está se referindo a B ou C). Não há necessidade, e nunca houve necessidade, de definir explicitamente as referências A-> B ou B-> C como nulas, por exemplo.

Tirando isso, na maioria das vezes o problema realmente não surge, porque na realidade você está lidando com objetos em coleções. Geralmente, você deve sempre pensar em remover objetos de listas, mapas etc. chamando o método remove () apropriado.

O caso em que costumava haver algum conselho para definir referências para nulo foi especificamente em um escopo longo, onde um objeto de uso intensivo de memória deixou de ser usado no meio do escopo . Por exemplo:

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;             <-- explicitly set to null
  doSomethingElse();
}

O raciocínio aqui era que, como obj ainda está no escopo, sem o anular explícito da referência, ele não se torna o lixo coletável até que o método doSomethingElse () seja concluído. E este é o conselho que provavelmente não se aplica mais às JVMs modernas : acontece que o compilador JIT pode descobrir em que ponto uma determinada referência de objeto local não é mais usada.

Neil Coffey
fonte
2
Variáveis ​​locais podem ser otimizadas pela máquina. Variáveis ​​de classe não podem. Já vi threads de trabalho (em um pool de threads) não liberarem seus objetos de estado após manipular a solicitação e, assim, fixar esses objetos na memória até que a thread de trabalho receba uma nova tarefa (que substitui imediatamente os objetos de estado antigos por novos). Com grandes objetos de estado e centenas de threads de trabalho (pense: grande servidor http), isso pode ser uma grande quantidade de memória mantida e copiada, mas nunca será usada novamente.
Brian White
1
Eu provavelmente deveria apenas adicionar: o conselho que dou acima é o conselho a seguir geralmente, assumindo bibliotecas bem comportadas, uma VM bem comportada etc. Se você descobrir que tem, digamos, uma biblioteca de pool de threads que se pendura em objetos depois um trabalho foi concluído, bem ... esse é um bug na referida biblioteca de pool de threads que talvez pudesse ser contornado anulando uma referência de objeto, mas também pode ser contornado usando uma biblioteca de pool de threads menos bugada. Não tenho certeza de que tal bug mude os princípios gerais de design.
Neil Coffey
25

Não, não é um conselho obsoleto. Referências pendentes ainda são um problema, especialmente se você estiver, digamos, implementando um contêiner de matriz expansível ( ArrayListou semelhante) usando uma matriz pré-alocada. Elementos além do tamanho "lógico" da lista devem ser anulados, ou então não serão liberados.

Consulte Effective Java 2ª ed, Item 6: Eliminate Obsolete Object References.

Chris Jester-Young
fonte
É um problema de idioma ou um problema de implementação de VM?
Pablo Santa Cruz
4
É uma questão "semântica". Basicamente, como você pré-alocou um array, a VM vê isso. Ele não sabe nada sobre o tamanho "lógico" do seu contêiner. Digamos que você tenha um ArrayList de tamanho 10, apoiado por um array de 16 elementos. A VM não pode saber se os itens 10..15 não são realmente usados; se esses slots tiverem conteúdo, eles não serão liberados.
Chris Jester-Young
e fora das classes de contêiner? Na composição de objetos em que o objeto interno não é desalocado pelo objeto externo.
sal de
7
@sal A regra geral é que se você não puder fazer referência a ele, ele será coletado pelo lixo. Portanto, se o objeto externo contém uma referência a outro objeto, supondo que o objeto interno não tenha nenhuma outra referência, definir a única referência do objeto externo como null fará com que todo o objeto seja coletado como lixo, incluindo suas referências órfãs.
ubermensch
2
No mesmo item 6 que você mencionou: Anular as referências de objetos deve ser a exceção e não a norma.
ACV de
10

Campos de instância, elementos de matriz

Se houver uma referência a um objeto, ele não pode ser coletado como lixo. Especialmente se esse objeto (e todo o gráfico por trás dele) for grande, há apenas uma referência que está interrompendo a coleta de lixo e essa referência não é mais necessária, o que é uma situação lamentável.

Casos patológicos são o objeto que retém uma instância não missária para toda a árvore XML DOM que foi usada para configurá-lo, o MBean que não foi cancelado ou a única referência a um objeto de um aplicativo da web não implantado que impede que um carregador de classe inteiro seja descarregado .

Portanto, a menos que você tenha certeza de que o objeto que contém a própria referência será coletado como lixo de qualquer maneira (ou mesmo assim), você deve anular tudo o que não é mais necessário.

Variáveis ​​com escopo:

Se você está considerando definir uma variável local como nula antes do final de seu escopo, para que ela possa ser recuperada pelo coletor de lixo e marcá-la como "inutilizável de agora em diante", você deve considerar colocá-la em um escopo mais limitado. .

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;          //   <-- explicitly set to null
  doSomethingElse();
}

torna-se

{
  {  
     BigObject obj = ...
     doSomethingWith(obj);
  }    //         <-- obj goes out of scope
  doSomethingElse();
}

Escopos longos e simples geralmente são ruins para a legibilidade do código também. A introdução de métodos privados para separar as coisas apenas para esse propósito também não é algo inédito.

Thilo
fonte
Se você está se referindo a uma referência forte, sim, isso é verdade, mas não para todas as referências. Referências fracas em java podem ser coletadas como lixo.
James Drinkard
5

Em ambientes com restrição de memória (por exemplo, telefones celulares), isso pode ser útil. Definindo null, o objeto não precisa esperar que a variável saia do escopo para ser gc'd.

Para a programação diária, entretanto, esta não deve ser a regra, exceto em casos especiais como o que Chris Jester-Young citou.

besen
fonte
1

Em primeiro lugar, não significa nada para o qual você está definindo um objeto null. Eu explico abaixo:

List list1 = new ArrayList();
List list2 = list1;

No segmento de código acima, estamos criando o nome list1da variável de referência do ArrayListobjeto do objeto que está armazenado na memória. Então list1está se referindo a esse objeto e ele nada mais do que uma variável. E na segunda linha de código, estamos copiando a referência de list1para list2. Agora, voltando à sua pergunta se eu faço:

list1 = null;

isso significa que list1não está mais se referindo a nenhum objeto armazenado na memória, então list2também não terá nada para se referir. Portanto, se você verificar o tamanho de list2:

list2.size(); //it gives you 0

Então aqui chega o conceito de coletor de lixo que diz «nada para se preocupar em liberar a memória que é mantida pelo objeto, farei isso quando descobrir que ele não será mais usado no programa e o JVM me gerenciará.»

Espero que o conceito esteja claro.

Aman Goel
fonte
0

Um dos motivos para isso é eliminar referências de objetos obsoletos. Você pode ler o texto aqui.

Sudip Bhandari
fonte