¿Establecer objetos Java en nulos ya hace algo?

112

Estaba hojeando algunos libros antiguos y encontré una copia de "Practical Java" de Peter Hagger. En la sección de rendimiento, hay una recomendación para establecer referencias a objetos nullcuando ya no se necesiten.

En Java, ¿establecer referencias a objetos para nullmejorar el rendimiento o la eficiencia de la recolección de basura? Si es así, ¿en qué casos es un problema? ¿Clases de contenedores? Composición de objetos ¿Clases internas anónimas?

Veo esto en el código con bastante frecuencia. ¿Es este consejo de programación obsoleto o sigue siendo útil?

sal
fuente
2
Perfilarlo. En tiempos de ejecución modernos, no debería ver ningún aumento significativo en el rendimiento o la huella de memoria.
Jason Coco
12
@Jason, perfilarlo? Eso supone que perfilaré un conjunto de casos lo suficientemente grande como para obtener un conjunto de resultados suficientemente bueno para responder a esto. Y que no elijo un conjunto de casos en los que la VM está lo suficientemente optimizada como para enmascarar los problemas de rendimiento y de GC. Por eso pregunto esto aquí. Para tener una idea de los casos en los que esto es un problema.
sal
1
Duplicado de stackoverflow.com/questions/449409/… .
Michael Myers

Respuestas:

73

Depende un poco de cuándo estabas pensando en anular la referencia.

Si tiene una cadena de objetos A-> B-> C, una vez que A no sea accesible, A, B y C serán elegibles para la recolección de basura (asumiendo que nada más se refiere a B o C). No es necesario, y nunca ha sido necesario, establecer explícitamente las referencias A-> B o B-> C en nulo, por ejemplo.

Aparte de eso, la mayoría de las veces el problema no surge realmente, porque en realidad se trata de objetos en colecciones. Por lo general, siempre debería pensar en eliminar objetos de listas, mapas, etc. llamando al método remove () apropiado.

El caso en el que solía haber algunos consejos para establecer referencias a nulo fue específicamente en un alcance largo en el que un objeto de memoria intensiva dejó de usarse en la mitad del alcance . Por ejemplo:

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

El razonamiento aquí fue que debido a que obj todavía está en el alcance, sin la anulación explícita de la referencia, no se puede recolectar como basura hasta que se complete el método doSomethingElse () . Y este es el consejo que probablemente ya no se aplica a las JVM modernas : resulta que el compilador JIT puede determinar en qué punto ya no se usa una referencia de objeto local determinada.

Neil Coffey
fuente
2
La máquina puede optimizar las variables locales. Las variables de clase no pueden. He visto subprocesos de trabajo (en un grupo de subprocesos) que no liberan sus objetos de estado después de manejar la solicitud y, por lo tanto, anclan esos objetos en la memoria hasta que al subproceso de trabajo se le dio una nueva tarea (que sobrescribió rápidamente los objetos de estado antiguos con otros nuevos). Con objetos de estado grandes y cientos de subprocesos de trabajo (piense en un servidor http grande), esto puede ser una gran cantidad de memoria almacenada y copiada, pero nunca se volverá a utilizar.
Brian White
1
Probablemente debería agregar: el consejo que doy arriba es el consejo a seguir en general, asumiendo bibliotecas que se comportan bien, una máquina virtual con buen comportamiento, etc. un trabajo ha terminado, bueno ... ese es un error en dicha biblioteca de grupo de subprocesos que quizás podría solucionarse anulando una referencia de objeto, pero igualmente podría solucionarse utilizando una biblioteca de grupo de subprocesos con menos errores. No estoy seguro de que tal error cambie los principios generales de diseño.
Neil Coffey
25

No, no es un consejo obsoleto. Las referencias colgantes siguen siendo un problema, especialmente si, por ejemplo, está implementando un contenedor de matriz expandible ( ArrayListo similar) utilizando una matriz preasignada. Los elementos más allá del tamaño "lógico" de la lista deben anularse, o de lo contrario no se liberarán.

Consulte Effective Java 2nd ed, Item 6: Elimine Obsolete Object References.

Chris Jester-Young
fuente
¿Se trata de un problema de idioma o de implementación de VM?
Pablo Santa Cruz
4
Es una cuestión "semántica". Básicamente, debido a que ha preasignado una matriz, la VM lo ve. No sabe nada sobre el tamaño "lógico" de su contenedor. Supongamos que tiene una ArrayList de tamaño 10, respaldada por una matriz de 16 elementos. La máquina virtual no puede saber que los elementos 10..15 no se utilizan realmente; si esas ranuras tienen contenido, no se liberarán.
Chris Jester-Young
¿qué pasa fuera de las clases de contenedores? En la composición de objetos donde el objeto interno no es desasignado por el objeto externo es.
sal
7
@sal La regla general es que si no puede hacer referencia a él, se recolecta la basura. Entonces, si el objeto externo contiene una referencia a otro objeto, asumiendo que el objeto interno no tiene ninguna otra referencia, establecer la única referencia del objeto externo en nulo hará que todo el objeto sea recolectado como basura, incluidas sus referencias huérfanas.
ubermensch
2
En el mismo ítem 6 mencionaste: Anular las referencias a objetos debería ser la excepción y no la norma.
ACV
10

Campos de instancia, elementos de matriz

Si hay una referencia a un objeto, no se puede recolectar basura. Especialmente si ese objeto (y todo el gráfico detrás de él) es grande, solo hay una referencia que detiene la recolección de basura, y esa referencia ya no es realmente necesaria, esa es una situación desafortunada.

Los casos patológicos son el objeto que conserva una instancia no necesaria para todo el árbol DOM XML que se usó para configurarlo, el MBean que no se eliminó del registro o la referencia única a un objeto de una aplicación web no implementada que evita que se descargue un cargador de clases completo .

Entonces, a menos que esté seguro de que el objeto que contiene la referencia en sí será recolectado como basura de todos modos (o incluso entonces), debe anular todo lo que ya no necesita.

Variables de ámbito:

Si está considerando establecer una variable local en nula antes de que finalice su alcance, para que el recolector de basura pueda reclamarla y marcarla como "inutilizable a partir de ahora", debería considerar colocarla en un alcance más limitado. .

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

se convierte en

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

Los ámbitos largos y planos también son generalmente malos para la legibilidad del código. La introducción de métodos privados para dividir las cosas solo con ese propósito tampoco es inaudita.

Thilo
fuente
Si se refiere a una referencia fuerte, sí, esto es cierto, pero no para todas las referencias. Las referencias débiles en Java se pueden recolectar como basura.
James Drinkard
5

En entornos con restricciones de memoria (por ejemplo, teléfonos móviles) esto puede resultar útil. Al establecer un valor nulo, el objeto no necesita esperar a que la variable salga del alcance para ser gc.

Para la programación diaria, sin embargo, esta no debería ser la regla, excepto en casos especiales como el que citó Chris Jester-Young.

besen
fuente
1

En primer lugar, no significa nada para lo que esté configurando un objeto null. Te lo explico a continuación:

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

En el segmento de código anterior, estamos creando el nombre list1de la variable de referencia del ArrayListobjeto del objeto que se almacena en la memoria. Entonces list1se refiere a ese objeto y no es más que una variable. Y en la segunda línea de código estamos copiando la referencia de list1a list2. Entonces, volviendo a tu pregunta si lo hago:

list1 = null;

eso significa que list1ya no se refiere a ningún objeto que esté almacenado en la memoria, por list2lo que tampoco tendrá nada que hacer referencia. Entonces, si verifica el tamaño de list2:

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

Así que aquí llega el concepto de recolector de basura que dice «no tienes nada de qué preocuparte por liberar la memoria que tiene el objeto, lo haré cuando descubra que ya no se usará en programa y JVM me administrará».

Espero que quede claro el concepto.

Aman Goel
fuente
0

Una de las razones para hacerlo es eliminar las referencias a objetos obsoletos. Puedes leer el texto aquí.

Sudip Bhandari
fuente