¿Alguna vez ha utilizado PhantomReference en algún proyecto?

89

Lo único que sé PhantomReferencees

  • Si usa su get()método, siempre regresará nully no el objeto. ¿De qué sirve?
  • Al usar PhantomReference, te aseguras de que el objeto no se pueda resucitar del finalizemétodo.

Pero, ¿de qué sirve este concepto / clase?

¿Ha usado esto alguna vez en alguno de sus proyectos o tiene algún ejemplo en el que deberíamos usarlo?

Rakesh Juyal
fuente
Como no puede obtener el obj referido de una PhantomReference, es un nombre completamente inapropiado: debería haber sido llamado FakeReferenceo NonReference.
Pacerier
Aquí hay otro hilo con código: stackoverflow.com/q/43311825/632951
Pacerier

Respuestas:

47

Usé PhantomReferences en un tipo de perfilador de memoria simplista y muy especializado para monitorear la creación y destrucción de objetos. Los necesitaba para realizar un seguimiento de la destrucción. Pero el enfoque está desactualizado. (Fue escrito en 2004 con el objetivo de J2SE 1.4.) Las herramientas de creación de perfiles profesionales son mucho más poderosas y confiables y las características más nuevas de Java 5 como JMX o agentes y JVMTI también se pueden usar para eso.

PhantomReferences (siempre utilizados junto con la cola de referencia) son superiores a lo finalizeque tiene algunos problemas y, por lo tanto, deben evitarse. Principalmente hacer que los objetos sean accesibles nuevamente. Esto podría evitarse con el modismo guardián del finalizador (-> leer más en 'Java efectivo'). Así que también son los nuevos finalizados .

Además, PhantomReferences

le permiten determinar exactamente cuándo se eliminó un objeto de la memoria. De hecho, son la única forma de determinarlo. En general, esto no es tan útil, pero puede ser útil en ciertas circunstancias muy específicas, como la manipulación de imágenes grandes: si está seguro de que una imagen debe ser recolectada como basura, puede esperar hasta que realmente lo sea antes de intentar cargar la siguiente imagen. y, por lo tanto, hacer que el temido OutOfMemoryError sea menos probable. (Citado de Enicholas .)

Y como psd escribió primero, Roedy Green tiene un buen resumen de referencias .

Peter Kofler
fuente
21

Una explicación general de la tabla cortada en cubitos , del Glosario de Java.

Que por supuesto coincide con la documentación de PhantomReference :

Objetos de referencia fantasma, que se colocan en cola después de que el recopilador determina que sus referentes pueden ser reclamados. Las referencias fantasmas se utilizan con mayor frecuencia para programar acciones de limpieza pre-mortem de una manera más flexible de lo que es posible con el mecanismo de finalización de Java.

Y por último, pero no menos importante, todos los detalles sangrientos ( esta es una buena lectura ): Objetos de referencia de Java (o Cómo aprendí a dejar de preocuparme y amar OutOfMemoryError) .

Codificación feliz. (Pero para responder a la pregunta, solo he usado WeakReferences).


fuente
Por cierto, una pregunta sobre ese artículo. En la sección sobre PhantomReference, mantiene una fuerte referencia a los objetos de conexión a través de esas dos tablas. Significa que las conexiones nunca serán inalcanzables (suponiendo que la instancia del grupo en sí nunca se vuelva inalcanzable). Entonces, las PhantomReferences correspondientes nunca se pondrán en cola, ¿verdad? ¿O me estoy perdiendo algo?
shrini1000
1
Vaya, ese artículo de kdgregory merece un +10
Pacerier
14

Gran explicación del uso de Phantom Reference:

Las referencias fantasmas son una forma segura de saber que un objeto se ha eliminado de la memoria. Por ejemplo, considere una aplicación que se ocupa de imágenes grandes. Supongamos que queremos cargar una imagen grande en la memoria cuando la imagen grande ya está en la memoria y está lista para la recolección de basura. En tal caso, queremos esperar hasta que se recopile la imagen anterior antes de cargar una nueva. Aquí, la referencia fantasma es una opción flexible y segura para elegir. La referencia de la imagen anterior se pondrá en cola en ReferenceQueue una vez que se finalice el objeto de imagen anterior. Después de recibir esa referencia, podemos cargar la nueva imagen en la memoria.

Sergii Shevchyk
fuente
12

Encontré un caso de uso práctico y útil PhantomReferenceque está org.apache.commons.io.FileCleaningTrackeren el proyecto commons-io. FileCleaningTrackereliminará el archivo físico cuando su objeto marcador sea recolectado como basura.
Algo a tener en cuenta es la Trackerclase que amplía la PhantomReferenceclase.

Tan Hui Onn
fuente
5

¡ESTO DEBE SER OBSOLETO CON JAVA 9!
¡Úselo en su java.util.Cleanerlugar! (O sun.misc.Cleaneren JRE más antiguo)

Publicación original:


Descubrí que el uso de PhantomReferences tiene casi la misma cantidad de trampas que los métodos de finalizador (pero menos problemas una vez que lo haces bien). He escrito una pequeña solución (un marco muy pequeño para usar PhantomReferences) para Java 8. Permite usar expresiones lambda como devoluciones de llamada que se ejecutarán después de que se haya eliminado el objeto. Puede registrar las devoluciones de llamada para recursos internos que deben cerrarse. Con esto he encontrado una solución que me funciona ya que la hace mucho más práctica.

https://github.com/claudemartin/java-cleanup

Aquí hay un pequeño ejemplo para mostrar cómo se registra una devolución de llamada:

  class Foo implements Cleanup {
    //...  
    public Foo() { 
    //...    
      this.registerCleanup((value) -> {
        try {
          // 'value' is 'this.resource'
          value.close();
        } catch (Exception e) {
          logger.warning("closing resource failed", e);
        }
      }, this.resource);
    }

Y luego está el método aún más simple para el cierre automático, haciendo aproximadamente lo mismo que el anterior:

this.registerAutoClose(this.resource);

Para responder tu pregunta:

[entonces para qué sirve]

No puedes limpiar algo que no existe. Pero podría haber tenido recursos que aún existen y deben limpiarse para poder eliminarlos.

Pero, ¿de qué sirve este concepto / clase?

No es necesariamente para hacer nada con ningún efecto que no sea depurar / registrar. O quizás por estadísticas. Lo veo más como un servicio de notificación del GC. También podría querer usarlo para eliminar datos agregados que se vuelven irrelevantes una vez que se elimina el objeto (pero probablemente haya mejores soluciones para eso). Los ejemplos a menudo mencionan que las conexiones de la base de datos deben cerrarse, pero no veo cómo esto es una buena idea ya que no podría trabajar con transacciones. Un marco de aplicación proporcionará una solución mucho mejor para eso.

¿Alguna vez ha usado esto en alguno de sus proyectos, o tiene algún ejemplo en el que deberíamos usar esto? ¿O este concepto está hecho solo para el punto de vista de la entrevista;)

Lo uso principalmente solo para registrar. Entonces puedo rastrear los elementos eliminados y ver cómo funciona GC y cómo se puede modificar. No ejecutaría ningún código crítico de esta manera. Si es necesario cerrar algo, debe hacerlo en una declaración try-with-resource-statement. Y lo uso en pruebas unitarias, para asegurarme de que no tengo pérdidas de memoria. De la misma manera que lo hace jontejj. Pero mi solución es un poco más general.

Claude martin
fuente
Sí, al igual que mi solución "java-cleanup". Ambas son abstracciones, por lo que no es necesario que las trate directamente.
Claude Martin
3

Usé una PhantomReference en una prueba unitaria para verificar que el código bajo prueba no guardaba referencias desagradables a algún objeto. ( Código original )

import static com.google.common.base.Preconditions.checkNotNull;
import static org.fest.assertions.Assertions.assertThat;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

import com.google.common.testing.GcFinalization;

/**
* Helps to test for memory leaks
*/
public final class MemoryTester
{
private MemoryTester()
{
}

/**
* A simple {@link PhantomReference} that can be used to assert that all references to it is
* gone.
*/
public static final class FinalizationAwareObject extends PhantomReference<Object>
{
private final WeakReference<Object> weakReference;

private FinalizationAwareObject(Object referent, ReferenceQueue<Object> referenceQueue)
{
super(checkNotNull(referent), referenceQueue);
weakReference = new WeakReference<Object>(referent, referenceQueue);
}

/**
* Runs a full {@link System#gc() GC} and asserts that the reference has been released
* afterwards
*/
public void assertThatNoMoreReferencesToReferentIsKept()
{
String leakedObjectDescription = String.valueOf(weakReference.get());
GcFinalization.awaitFullGc();
assertThat(isEnqueued()).as("Object: " + leakedObjectDescription + " was leaked").isTrue();
}
}

/**
* Creates a {@link FinalizationAwareObject} that will know if {@code referenceToKeepTrackOff}
* has been garbage collected. Call
* {@link FinalizationAwareObject#assertThatNoMoreReferencesToReferentIsKept()} when you expect
* all references to {@code referenceToKeepTrackOff} be gone.
*/
public static FinalizationAwareObject createFinalizationAwareObject(Object referenceToKeepTrackOff)
{
return new FinalizationAwareObject(referenceToKeepTrackOff, new ReferenceQueue<Object>());
}
}

Y la prueba :

@Test
public void testThatHoldingOnToAnObjectIsTreatedAsALeak() throws Exception
{
    Object holdMeTight = new String("Hold-me-tight");
    FinalizationAwareObject finalizationAwareObject = MemoryTester.createFinalizationAwareObject(holdMeTight);
    try
    {
    finalizationAwareObject.assertThatNoMoreReferencesToReferentIsKept();
    fail("holdMeTight was held but memory leak tester did not discover it");
    }
    catch(AssertionError expected)
    {
    assertThat(expected).hasMessage("[Object: Hold-me-tight was leaked] expected:<[tru]e> but was:<[fals]e>");
    }
}
jontejj
fuente
2

Es común usarlo WeakReferencedonde PhantomReferencesea ​​más apropiado. Esto evita ciertos problemas de poder resucitar objetos después WeakReferencede que el recolector de basura haya borrado / puesto en cola. Por lo general, la diferencia no importa porque la gente no juega a ser tontos.

El uso PhantomReferencetiende a ser un poco más intrusivo porque no se puede fingir que el getmétodo funciona. No puede, por ejemplo, escribir un Phantom[Identity]HashMap.

Tom Hawtin - tackline
fuente
IdentityHashMap <PhantomReference> es en realidad uno de los lugares apropiados para IdentityHashMap. Tenga en cuenta que se mantuvo una fuerte referencia a PhantomReference, pero no al referente .
¿Realmente quiere decir que para referencias débiles, finalize puede recrear el obj? weakref.getpodría volver null, y luego, más tarde, todavía puede devolver el obj?
Pacerier
@Pacerier finalizeno recrea el objeto como tal. Puede hacer que el objeto sea fácilmente accesible de nuevo después de un WeakReferenceretorno nullde gety se pone en cola. / (user166390: como en un mapa con clave en el objetivo de la referencia, como lo WeakHashMaphace, no un mapa de identidad de referencias, lo cual está bien)
Tom Hawtin - línea de entrada
1

si usa su método get (), siempre devolverá null, y no el objeto. [entonces para qué sirve]

Los métodos útiles para llamar (en lugar de get()) serían isEnqueued()o referenceQueue.remove(). Llamaría a estos métodos para realizar alguna acción que debe tener lugar en la ronda final de recolección de basura del objeto.

La primera vez es cuando el objeto tiene su finalize()método llamado, por lo que también podría poner ganchos de cierre allí. Sin embargo, como han dicho otros, probablemente hay formas más seguras de realizar la limpieza o cualquier acción que deba realizarse antes y después de la recolección de basura o, más en general, al final de la vida útil del objeto.


fuente
1

Encontré otro uso práctico de PhantomReferencesen la clase LeakDetector de Jetty.

Jetty usa LeakDetectorclass para detectar si el código de cliente adquiere un recurso pero nunca lo libera y la LeakDetectorclase usa el PhantomReferencespara este propósito.

Sahil Chhabra
fuente