2009-10-21 12 views
69

Lo único que sé de PhantomReference es,¿Alguna vez ha usado PhantomReference en algún proyecto?

  • Si utiliza su método get(), siempre devolverá null y no el objeto. ¿De qué sirve?
  • Al usar PhantomReference, se asegura de que el objeto no se puede resucitar del finalize método.

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

¿Alguna vez ha usado esto en alguno de sus proyectos o tiene algún ejemplo donde deberíamos usar esto?

+0

También https://stackoverflow.com/a/9827686/632951 – Pacerier

+0

Como no se puede obtener el referido obj de una referencia fantasma, es un nombre incorrecto: se debería haber llamado 'FakeReference' o' NonReference'. – Pacerier

+0

Aquí hay otro hilo con código: https://stackoverflow.com/q/43311825/632951 – Pacerier

Respuesta

42

que utilizan PhantomReference s en un simplistic, very specialized kind of memory profiler para supervisar la creación de objetos y la destrucción. Los necesitaba para hacer 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 potentes y confiables, y las funciones más nuevas de Java 5, como JMX o agentes, y JVMTI también se pueden usar para eso.

PhantomReference s (siempre utilizados junto con la cola de referencia) son superiores a finalize que tiene algunos problemas y, por lo tanto, deben evitarse. Principalmente haciendo que los objetos sean alcanzables nuevamente. Esto podría evitarse con la expresión del guardián del finalizador (-> leer más en 'Java efectivo'). Por lo tanto, también son el nuevo finalizar.

Además, PhantomReference s

le permiten determinar exactamente cuando un objeto se elimina de la memoria. Ellos son de hecho la única forma de determinar eso. Esto generalmente no es tan útil como , pero puede ser útil en ciertas circunstancias muy específicas como manipular imágenes de gran tamaño: si está seguro de que una imagen debe ser basura recolectada, puede esperar hasta que esté realmente antes de intentar carga la siguiente imagen, y por lo tanto hace que el temido OutOfMemoryError sea menos probable. (Citado de enicholas.)

Y como psd escribió primero, Roedy verde tiene un good summary of references.

18

A general diced-up table explanation, del Java Glossary.

que por supuesto coincide con el PhantomReference documentation:

objetos de referencia Phantom, que están en cola después de que el colector determina que sus referentes de lo contrario se pueden reutilizar. Las referencias ficticias se utilizan con mayor frecuencia para programar acciones de limpieza premortem de una manera más flexible que la que es posible con el mecanismo de finalización de Java.

Y por último pero no menos importante, todos los detalles morbosos (esta es una buena lectura): Java Reference Objects (or How I Learned to Stop Worrying and Love OutOfMemoryError).

Happy coding. (WeakReferences Pero para responder a la pregunta, sólo he utilizado siempre.)

+0

Por cierto, una pregunta sobre ese artículo. En la sección sobre PhantomReference, él mantiene una fuerte referencia a los objetos de conexión a través de esas dos tablas. Significa que las conexiones nunca llegarán a ser inalcanzables (suponiendo que la instancia del grupo nunca se vuelva inalcanzable). Entonces, las referencias Phantom correspondientes nunca se pondrán en cola, ¿verdad? ¿O me estoy perdiendo algo? – shrini1000

+0

Vaya, ese artículo de kdgregory merece un +10 – Pacerier

2

Es común usar WeakReference donde PhantomReference es más apropiado. Esto evita ciertos problemas de poder resucitar objetos después de que un WeakReference es borrado/en cola por el recolector de basura. Por lo general, la diferencia no importa porque la gente no está jugando a tontos.

El uso de PhantomReference tiende a ser un poco más intrusivo porque no se puede pretender que el método get funciona. No puede, por ejemplo, escribir un Phantom[Identity]HashMap.

+0

IdentityHashMap es en realidad uno de los lugares apropiados para IdentityHashMap. Tenga en cuenta que * strong reference * se mantuvo en PhantomReference, * pero no en el referente *. –

+0

¿O lo leí completamente mal? Me disculpo si lo hice. –

+0

¿De verdad quiere decir que para las referencias débiles, finalizar puede volver a crear el obj? 'weakref.get' podría devolver' null', y luego, aún puede devolver el obj? – Pacerier

9

Encontré un caso de uso práctico y útil de PhantomReference que es org.apache.commons.io.FileCleaningTracker en el proyecto commons-io. FileCleaningTracker eliminará el archivo físico cuando su objeto marcador sea basura.
Algo a tener en cuenta es la clase Tracker que extiende la clase PhantomReference.

3

Utilicé una PhantomReference en una prueba unitaria para verificar que el código bajo prueba no guardara referencias exclusivas a algún objeto. (Original code)

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 el test:

@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>"); 
    } 
} 
7

Great explanation de Phantom uso Referencia:

referencias son Phantom manera segura de conocer un objeto que 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 la imagen anterior sea recopilada antes de cargar una nueva. Aquí, la referencia ficticia es opción flexible y segura para elegir. La referencia de la imagen anterior se pondrá en cola en la ReferenceQueue una vez que el objeto de la imagen anterior sea finalizado. Después de recibir esa referencia, podemos cargar la nueva imagen en la memoria.

3

ESTO DEBE SER OBSOLETO CON JAVA 9!
¡Use java.util.Cleaner en su lugar! (O sun.misc.Cleaner en JRE más)

Original post:


me encontré con que el uso de PhantomReferences tiene casi la misma cantidad de trampas como los métodos Finalizer (pero un menor número de problemas una vez que lo haga 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 los 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 ha registrado 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 sencillo para el cierre automático, haciendo casi lo mismo que el anterior:

this.registerAutoClose(this.resource); 

para responder a sus preguntas:

[entonces cuál es el uso de la misma]

No se puede limpiar algo que no existe. Pero podría haber tenido recursos que aún existen y necesitan ser limpiados para que puedan ser eliminados.

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

No es necesariamente para hacer nada con otro efecto que no sea la depuración/registro. O tal vez para las estadísticas. Lo veo más como un servicio de notificación del GC. También podría querer usarlo para eliminar los 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 conexiones de bases de datos que deben cerrarse, pero no veo cómo esta es una buena idea ya que no se puede trabajar con transacciones. Un marco de aplicación proporcionará una solución mucho mejor para eso.

¿Alguna vez ha utilizado esto en cualquiera de sus proyectos, o tiene algún ejemplo donde deberíamos usar esto? ¿O es este concepto hecho para el punto de vista de la entrevista;)

Lo uso principalmente para el registro. Así que puedo rastrear los elementos eliminados y ver cómo funciona GC y se puede ajustar. No ejecutaría ningún código crítico de esta manera. Si algo necesita ser cerrado, entonces debe hacerse en una declaración try-with-resource. Y lo uso en pruebas unitarias, para asegurarme de que no haya pérdidas de memoria. De la misma manera que jontejj lo hace. Pero mi solución es un poco más general.

+0

Bueno,' java.util.Cleaner' internamente utiliza PhantomReferences de todos modos ... – Pacerier

+0

Sí, al igual que mi solución "java-cleanup". Ambas son abstracciones, por lo que no es necesario tratarlas directamente. –

1

si utiliza su método get() siempre devolverá nulo, y no el objeto . [Entonces cuál es el uso de la misma]

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

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

Cuestiones relacionadas