Avez-vous déjà utilisé PhantomReference dans un projet?

89

La seule chose que je sais PhantomReferenceest,

  • Si vous utilisez sa get()méthode, elle retournera toujours nullet non l'objet. Quelle en est l'utilité?
  • En utilisant PhantomReference, vous vous assurez que l'objet ne peut pas être ressuscité à partir de finalizemethod.

Mais à quoi sert ce concept / cette classe?

Avez-vous déjà utilisé cela dans l'un de vos projets ou avez-vous un exemple où nous devrions l'utiliser?

Rakesh Juyal
la source
Comme vous ne pouvez pas obtenir l'objet référencé d'un PhantomReference, c'est un abus de langage complet: il aurait dû être appelé FakeReferenceou NonReference.
Pacerier
Voici un autre fil avec le code: stackoverflow.com/q/43311825/632951
Pacerier

Réponses:

47

J'ai utilisé PhantomReferences dans un type de profileur de mémoire simpliste et très spécialisé pour surveiller la création et la destruction d'objets. J'avais besoin d'eux pour suivre la destruction. Mais l'approche est dépassée. (Il a été écrit en 2004 pour J2SE 1.4.) Les outils de profilage professionnels sont beaucoup plus puissants et fiables et les nouvelles fonctionnalités Java 5 comme JMX ou les agents et JVMTI peuvent également être utilisées pour cela.

PhantomReferences (toujours utilisé avec la file d'attente de référence) sont supérieurs à ceux finalizequi posent certains problèmes et doivent donc être évités. Principalement rendre les objets accessibles à nouveau. Cela pourrait être évité avec l'idiome du gardien du finaliseur (-> en savoir plus dans 'Effective Java'). Donc, ils sont aussi les nouveaux finaliser .

En outre, PhantomReferences

vous permettent de déterminer exactement quand un objet a été supprimé de la mémoire. C'est en fait le seul moyen de le déterminer. Ce n'est généralement pas très utile, mais peut s'avérer utile dans certaines circonstances très spécifiques comme la manipulation de grandes images: si vous savez avec certitude qu'une image doit être récupérée, vous pouvez attendre qu'elle le soit réellement avant d'essayer de charger l'image suivante. , et donc rendre le OutOfMemoryError redouté moins probable. (Cité d' énicholas .)

Et comme psd l'a écrit en premier, Roedy Green a un bon résumé des références .

Peter Kofler
la source
21

Une explication générale de la table en dés , tirée du glossaire Java.

Ce qui coïncide bien sûr avec la documentation PhantomReference :

Objets de référence fantômes, qui sont mis en file d'attente après que le collecteur a déterminé que leurs référents pourraient autrement être récupérés. Les références fantômes sont le plus souvent utilisées pour planifier des actions de nettoyage pré-mortem d'une manière plus flexible que ce n'est possible avec le mécanisme de finalisation Java.

Et le dernier mais non le moindre, tous les détails sanglants ( c'est une bonne lecture ): Objets de référence Java (ou comment j'ai appris à arrêter de m'inquiéter et à aimer OutOfMemoryError) .

Bon codage. (Mais pour répondre à la question, je n'ai jamais utilisé que des références faibles.)


la source
Btw, une question sur cet article. Dans la section sur PhantomReference, il garde une référence forte aux objets de connexion à travers ces deux tables. Cela signifie que les connexions ne deviendront jamais inaccessibles (en supposant que l'instance de pool elle-même ne devienne jamais inaccessible). Ainsi, les références PhantomReferences correspondantes ne seront jamais mises en file d'attente, non? Ou est-ce que je manque quelque chose?
shrini1000
1
Wow, cet article de kdgregory mérite un +10
Pacerier
14

Excellente explication de l'utilisation de Phantom Reference:

Les références fantômes sont un moyen sûr de savoir qu'un objet a été supprimé de la mémoire. Par exemple, considérons une application qui traite de grandes images. Supposons que nous souhaitons charger une grande image en mémoire lorsqu'une grande image est déjà en mémoire, prête pour le ramasse-miettes. Dans ce cas, nous voulons attendre que l'ancienne image soit collectée avant d'en charger une nouvelle. Ici, la référence fantôme est une option flexible et sûre à choisir. La référence de l'ancienne image sera mise en file d'attente dans ReferenceQueue une fois que l'ancien objet image est finalisé. Après avoir reçu cette référence, nous pouvons charger la nouvelle image en mémoire.

Sergii Shevchyk
la source
12

J'ai trouvé un cas d'utilisation pratique et utile PhantomReferencequi est org.apache.commons.io.FileCleaningTrackerdans le projet commons-io. FileCleaningTrackersupprimera le fichier physique lorsque son objet marqueur sera récupéré.
Quelque chose à noter est la Trackerclasse qui étend la PhantomReferenceclasse.

Tan Hui Onn
la source
5

CELA DEVRAIT ÊTRE OBSOLÉ AVEC JAVA 9!
Utilisez java.util.Cleanerplutôt! (Ousun.misc.Cleaner sur un JRE plus ancien)

Message d'origine:


J'ai trouvé que l'utilisation de PhantomReferences a presque le même nombre de pièges que les méthodes de finalisation (mais moins de problèmes une fois que vous avez bien fait les choses). J'ai écrit une petite solution (un très petit framework pour utiliser PhantomReferences) pour Java 8. Cela permet d'utiliser des expressions lambda comme rappels à exécuter après la suppression de l'objet. Vous pouvez enregistrer les rappels pour les ressources internes qui doivent être fermées. Avec cela, j'ai trouvé une solution qui fonctionne pour moi car elle la rend beaucoup plus pratique.

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

Voici un petit exemple pour montrer comment un rappel est enregistré:

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

Et puis il y a la méthode encore plus simple pour la fermeture automatique, faisant à peu près la même chose que ci-dessus:

this.registerAutoClose(this.resource);

Pour répondre à tes questions:

[alors quelle est son utilisation]

Vous ne pouvez pas nettoyer quelque chose qui n'existe pas. Mais il aurait pu avoir des ressources qui existent toujours et doivent être nettoyées pour pouvoir être supprimées.

Mais à quoi sert ce concept / cette classe?

Il ne s'agit pas nécessairement de faire quoi que ce soit avec un effet autre que le débogage / la journalisation. Ou peut-être pour les statistiques. Je le vois plus comme un service de notification du GC. Vous pouvez également l'utiliser pour supprimer les données agrégées qui deviennent inutiles une fois l'objet supprimé (mais il existe probablement de meilleures solutions pour cela). Les exemples mentionnent souvent que les connexions à la base de données doivent être fermées, mais je ne vois pas en quoi c'est une si bonne idée que vous ne pouvez pas travailler avec des transactions. Un cadre d'application fournira une bien meilleure solution pour cela.

Avez-vous déjà utilisé cela dans l'un de vos projets, ou avez-vous un exemple où nous devrions l'utiliser? Ou ce concept est-il fait uniquement pour le point de vue de l'entrevue;)

Je l'utilise principalement pour la journalisation. Je peux donc retracer les éléments supprimés et voir comment GC fonctionne et peut être modifié. Je n'exécuterais aucun code critique de cette manière. Si quelque chose doit être fermé, cela doit être fait dans une instruction try-with-resource. Et je l'utilise dans les tests unitaires, pour m'assurer de ne pas avoir de fuites de mémoire. De la même manière que jontejj le fait. Mais ma solution est un peu plus générale.

Claude Martin
la source
Oui, tout comme ma solution "java-cleanup". Les deux sont des abstractions, vous n'avez donc pas besoin de les traiter directement.
Claude Martin
3

J'ai utilisé un PhantomReference dans un test unitaire pour vérifier que le code sous test ne gardait pas de références indésirables à un objet. ( Code d'origine )

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>());
}
}

Et le 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>");
    }
}
jontejj
la source
2

Il est courant d'utiliser WeakReferencelà où PhantomReferencec'est plus approprié. Cela évite certains problèmes de pouvoir ressusciter des objets après unWeakReference est effacé / mis en file d'attente par le garbage collector. Habituellement, la différence n'a pas d'importance parce que les gens ne jouent pas à des buggers stupides.

L'utilisation a PhantomReferencetendance à être un peu plus intrusive car vous ne pouvez pas prétendre que la getméthode fonctionne. Vous ne pouvez pas, par exemple, écrire un fichier Phantom[Identity]HashMap.

Tom Hawtin - Tacle
la source
IdentityHashMap <PhantomReference> est en fait l'un des emplacements appropriés pour un IdentityHashMap. Notez que la référence forte est restée à la PhantomReference, mais pas au référent .
Voulez-vous dire que pour les références faibles, finaliser peut recréer l'objet? weakref.getpourrait revenir null, et plus tard, il est toujours en mesure de retourner l'objet?
Pacerier
@Pacerier finalizene recrée pas l'objet en tant que tel. Il peut rendre l'objet à nouveau fortement accessible après un WeakReferenceretour nullde getet mis en file d'attente. / (user166390: Comme dans une carte saisie sur la cible de la référence, comme le WeakHashMapfait, pas une carte d'identité de références ce qui est bien.)
Tom Hawtin - tackline
1

si vous utilisez sa méthode get (), elle retournera toujours null, et non l'objet. [alors quelle est son utilisation]

Les méthodes utiles pour appeler (plutôt que get()) seraient isEnqueued()ou referenceQueue.remove(). Vous appelleriez ces méthodes pour effectuer une action qui doit avoir lieu lors du dernier tour de garbage collection de l'objet.

La première fois, c'est lorsque l'objet a sa finalize()méthode appelée, vous pouvez donc y placer des crochets de fermeture. Cependant, comme d'autres l'ont indiqué, il existe probablement des moyens plus sûrs d'effectuer le nettoyage ou toute autre action qui doit avoir lieu avant et après le ramassage des ordures ou, plus généralement, à la fin de la vie de l'objet.


la source
1

J'ai trouvé une autre utilisation pratique de PhantomReferencesdans la classe LeakDetector de Jetty.

Jetty utilise la LeakDetectorclasse pour détecter si le code client acquiert une ressource mais ne la libère jamais et la LeakDetectorclasse utilise le PhantomReferencesà cette fin.

Sahil Chhabra
la source