La seule chose que je sais sur PhantomReference
est,
get()
, elle renverra toujours null
et non l'objet. À quoi ça sert?PhantomReference
, vous vous assurez que l'objet ne peut pas être ressuscité à partir de la méthode finalize
.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?
J'ai utilisé PhantomReference
s dans un type simpliste et très spécialisé de profileur de mémoire pour surveiller la création et la destruction d'objets. J'en avais besoin pour suivre la destruction. Mais l'approche est dépassée. (Il a été écrit en 2004 ciblant 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.
PhantomReference
s (toujours utilisé avec la file d'attente de référence) est supérieur à finalize
qui a quelques problèmes et doit donc être évité. Rendre principalement les objets accessibles à nouveau. Cela pourrait être évité avec l'idiome du gardien du finaliseur (-> lire la suite dans 'Java efficace'). Ils sont donc aussi la nouvelle finalisation .
De plus, PhantomReference
s
vous permettent de déterminer exactement quand un objet a été supprimé de la mémoire. Ils sont en fait le seul moyen de le déterminer. Ce n'est généralement pas très utile, mais cela peut être utile dans certaines circonstances très spécifiques telles que 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 avant d'essayer de charger l'image suivante. , et donc rendre la redoutable OutOfMemoryError moins probable. (Cité de enicholas .)
Et comme psd l'a écrit en premier, Roedy Green a un bon résumé des références .
A explication générale du tableau découpé , tiré du 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 qui est possible avec le mécanisme de finalisation Java Java.
Et enfin et surtout, tous les détails sanglants ( c'est une bonne lecture ): Objets de référence Java (ou comment j'ai appris à arrêter) Inquiétude et amour OutOfMemoryError) .
Codage heureux. (Mais pour répondre à la question, je n'ai jamais utilisé que des références faibles.)
Grande explication d'utilisation de la référence fantôme:
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 des images de grande taille. Supposons que nous voulons charger une grande image dans la mémoire quand une grande image est déjà en mémoire, prête à être récupérée. 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 sera finalisé. Après avoir reçu cette référence, nous pouvons charger la nouvelle image dans la mémoire.
J'ai trouvé un cas d'utilisation pratique et utile de PhantomReference
qui est org.Apache.commons.io.FileCleaningTracker
dans le projet commons-io. FileCleaningTracker
supprimera le fichier physique lorsque son objet marqueur sera récupéré.
Un élément à prendre en compte est la classe Tracker
qui étend la classe PhantomReference
.
CECI DEVRAIT ÊTRE OBSOLÈTE AVEC Java 9!
Utilisation Java.util.Cleaner
au lieu! (Ou Sun.misc.Cleaner
sur JRE plus ancien)
Message d'origine:
J'ai trouvé que l'utilisation de PhantomReferences a presque le même nombre d'embûches que les méthodes de finalisation (mais moins de problèmes une fois que vous avez bien compris). J'ai écrit une petite solution (un très petit framework pour utiliser PhantomReferences) pour Java 8. Il permet d'utiliser des expressions lambda comme rappels à exécuter après la suppression de l'objet. Vous pouvez enregistrer le rappels pour les ressources internes qui devraient ê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 de fermeture automatique, faisant à peu près la même chose que ci-dessus:
this.registerAutoClose(this.resource);
Pour répondre à vos questions:
[alors à quoi ça sert]
Vous ne pouvez pas nettoyer quelque chose qui n'existe pas. Mais il aurait pu avoir des ressources qui existent encore et qui doivent être nettoyées pour pouvoir être supprimées.
Mais à quoi sert ce concept/cette classe?
Ce n'est pas nécessairement de faire quoi que ce soit d'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 des 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 la fermeture des connexions à la base de données, mais je ne vois pas en quoi c'est une si bonne idée que vous ne pourriez 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'entretien;)
Je l'utilise surtout juste pour la journalisation. Je peux donc suivre 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-statement. Et je l'utilise dans les tests unitaires, pour m'assurer que je n'ai pas 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.
J'ai utilisé un PhantomReference dans un test unitaire pour vérifier que le code testé ne gardait pas de références inutiles à 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>");
}
}
Il est courant d'utiliser WeakReference
où PhantomReference
est plus approprié. Cela évite certains problèmes de pouvoir ressusciter des objets après qu'un WeakReference
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 bouffons idiots.
L'utilisation de PhantomReference
a tendance à être un peu plus intrusive car vous ne pouvez pas prétendre que la méthode get
fonctionne. Vous ne pouvez pas, par exemple, écrire un Phantom[Identity]HashMap
.
si vous utilisez sa méthode get (), elle retournera toujours null, et non l'objet. [alors à quoi ça sert]
Les méthodes utiles à 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 cycle de récupération de place de l'objet.
La première fois, c'est lorsque l'objet a sa méthode finalize()
appelée, vous pouvez donc y placer des crochets de fermeture. Cependant, comme d'autres l'ont dit, il existe probablement des moyens plus sûrs d'effectuer le nettoyage ou toute action devant avoir lieu avant et après la collecte des ordures ou, plus généralement, en fin de vie de l'objet.