2009-08-25 7 views
29

Comment empêcher un objet d'être collecté?Comment empêcher un objet d'être collecté?

Existe-t-il des approches par finalisation ou par référence fantôme ou par d'autres approches?

On m'a posé cette question dans une interview. L'intervieweur a suggéré que finalize() peut être utilisé.

+0

S'il vous plaît fournir certains extraits de code –

+0

Rahul, vous dirigeaient « S'il vous plaît fournir des extraits de code » vers quelqu'un en particulier, ou étaient Voulez-vous mettre à jour les exigences de votre question? –

+0

Hey j'ai mis à jour mes questions –

Répondre

32

Conserver une référence. Si votre objet est collecté prématurément, c'est un symptôme que vous avez un bug dans la conception de votre application.

Le garbage collector recueille uniquement les objets pour lesquels il n'y a aucune référence dans votre application. S'il n'y a aucun objet qui référencerait naturellement l'objet collecté, demandez-vous pourquoi il devrait être maintenu en vie.

Une utilisation dans laquelle vous n'avez généralement aucune référence, mais qui souhaite conserver un objet est un singleton. Dans ce cas, vous pouvez utiliser une variable statique. Une mise en œuvre possible d'un singleton ressemblerait à ceci:

public class Singleton { 
    private static Singleton uniqueInstance; 

    private Singleton() { 
    } 

    public static synchronized Singleton getInstance() { 
    if (uniqueInstance == null) { 
     uniqueInstance = new Singleton(); 
    } 
    return uniqInstance; 
    } 
} 

Edit: Techniquement, vous pouvez stocker une référence quelque part dans votre finaliseur. Cela empêchera la collecte de l'objet jusqu'à ce que le collecteur détermine à nouveau qu'il n'y a plus de références. Le finalizer ne sera cependant appelé qu'une seule fois, vous devez donc vous assurer que votre objet (y compris ses superclasses) n'a pas besoin d'être finalisé après la première collecte. Je vous conseille cependant de ne pas utiliser cette technique dans les programmes actuels. (Il laissera des collègues comme moi crier WTF !?;)

protected void finalize() throws Throwable { 
    MyObjectStore.getInstance().store(this); 
    super.finalize(); // questionable, but you should ensure calling it somewhere. 
    } 
+0

Est-il possible de le faire dans la méthode finalize –

+2

Vous pouvez provoquer d'autres objets pour stocker une nouvelle référence à votre objet. Notez, cependant, que finalize est appelé au plus une fois pour un objet donné, il ne sera donc pas finalisé lorsque le collecteur détermine qu'il peut être collecté à nouveau. – Tobias

+0

"(Il va laisser des collègues comme moi hurlant WTF !?;)" Oui je sais mais il m'a été demandé par un intervieweur donc je n'ai aucune idée pourquoi il m'a posé cette question :) –

3

S'il y a encore une référence à l'objet, il ne sera pas les déchets ramassés. S'il n'y a aucune référence, vous ne devriez pas s'en soucier. En d'autres termes, le ramasse-miettes ne collecte que les ordures. Laisse faire son boulot.

+0

en d'autres termes - n'utilisez pas finalize() pour jouer des astuces sur le collecteur –

0

Je crois qu'il existe un modèle pour cela. Je ne sais pas si c'est le modèle d'usine. Mais vous avez un objet qui crée tous vos objets et contient une référence à eux. Lorsque vous en avez fini avec eux, vous les référencez en usine, ce qui rend l'appel explicite.

2

Je soupçonne que vous pourriez faire référence à si votre méthode finalize stagne une référence à l'objet en cours de finalisation. Dans ce cas (si ma lecture du Java Language Spec est correcte), la méthode finalize ne sera jamais relancée, mais l'objet ne sera pas encore récupéré.

Ce n'est pas le genre de chose que l'on fait dans la vraie vie, sauf peut-être par accident!

2

Cela ressemble à l'une de ces questions que l'on ne peut interroger que pendant un certain temps. La fonction finalize() est exécutée lorsque votre objet récupère les ordures. Il serait donc assez pervers de mettre quelque chose dedans pour empêcher la collecte. Normalement, vous n'avez qu'une référence et c'est tout ce dont vous avez besoin.

Je ne suis même pas sûr de ce qui se passerait si vous créiez une nouvelle référence pour quelque chose dans le finaliseur - puisque le garbage collector a déjà décidé de le collecter vous vous retrouveriez alors avec une référence nulle? On dirait une mauvaise idée, en tout cas. par exemple.

public class Foo { 
    static Foo reference; 
    ... 
    finalize(){ 
    reference = this; 
    } 
} 

Je doute que cela fonctionnerait, ou il pourrait fonctionner, mais dépendre de la GC implenetation, ou être « un comportement non spécifié ». Regarde mal, cependant.

9

L'astuce que votre enquêteur recherchait est probablement qu'il veut que vous sachiez que vous pouvez empêcher le ramassage des ordures d'enlever un objet en forçant une fuite de mémoire. De toute évidence, si vous gardez une référence à l'objet dans un contexte de longue durée, il ne sera pas collecté, mais ce n'est pas ce que le recruteur de l'OP a demandé. Ce n'est pas quelque chose qui arrive dans la méthode finalize.

Ce que vous pouvez faire pour empêcher la collecte des ordures à l'intérieur de la méthode finalize est d'écrire une boucle infinie, dans laquelle vous appelez Thread.yield(); (sans doute pour garder une boucle vide d'être optimisé loin):

@Override 
protected void finalize() throws Throwable { 
    while (true) { 
     Thread.yield(); 
    } 
} 

Mon référence ici est un article par Elliot Back, dans lequel décrit forçant une fuite de mémoire par cette méthode.

Juste une autre façon dont finaliser les méthodes sont mal.

+1

Je pense que le point n'était pas que "Thread.yield()" interrompait la collecte de place, mais que la boucle infinie l'avait fait - Thread.yield () étant là pour empêcher la boucle infinie de prendre trop de CPU. –

+0

@Score_Under Bonne prise! Merci, j'ai relu l'article et édité ma réponse. Merci encore. – CPerkins

1

Le point clé est que si nous définissons la variable de référence réelle pointant vers l'objet null, bien que nous ayons des variables d'instance de cette classe pointant vers cet objet non défini sur null. L'objet est automatiquement admissible à ordures collection.if sauver l'objet GC, utilisez ce code ...

public class GcTest { 

    public int id; 
    public String name; 
    private static GcTest gcTest=null; 

    @Override 
    protected void finalize() throws Throwable { 
     super.finalize(); 

     System.out.println("In finalize method."); 
     System.out.println("In finalize :ID :"+this.id); 
     System.out.println("In finalize :ID :"+this.name); 

     gcTest=this; 

    } 

    public static void main(String[] args) { 

     GcTest myGcTest=new GcTest(); 
     myGcTest.id=1001; 
     myGcTest.name="Praveen"; 
     myGcTest=null; 

     // requesting Garbage Collector to execute. 
     // internally GC uses Mark and Sweep algorithm to clear heap memory. 
     // gc() is a native method in RunTime class. 

     System.gc(); // or Runtime.getRuntime().gc(); 

     try { 
      Thread.sleep(2000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("\n------- After called GC() ---------\n"); 
     System.out.println("Id :"+gcTest.id); 
     System.out.println("Name :"+gcTest.name); 


    } 

} 

Sortie:

Dans la méthode finalize.
En Finaliser: ID: 1001
En Finaliser: ID: Praveen

------- Après appelé GC() --------

Id: 1001 Nom
: Praveen

3

La meilleure façon est d'utiliser Unsafe, bien que ByteBuffer pourrait être une solution de contournement possible dans certains cas.

Recherchez également le mot-clé "off-heap" mémoire.

non sécuritaires

Avantages sur ByteBuffer:

  • permet aux objets d'être représentés directement, sans pour sérialisation et donc plus rapide
  • sans vérification de limites, donc plus rapide
  • contrôle désallocation explicite
  • peut allouer plus que la limite JVM

Il n'est cependant pas facile de travailler.La méthode est décrite dans les articles suivants:

Ils se composent des étapes suivantes:

  • nous avons besoin d'un opérateur sizeof , qui n'a pas de sécurité. Comment faire un a été demandé à: In Java, what is the best way to determine the size of an object?. Les meilleures options est probablement l'API instrument, mais que vous devez créer un pot et utiliser les options de ligne de commande spéciale ...

  • une fois que nous avons sizeof, allouer suffisamment de mémoire avec Unsafe#allocateMemory, qui est essentiellement un malloc et renvoie un adresse

  • créer un objet régulier sur le tas, copiez-le dans la mémoire allouée avec Unsafe#copyMemory. Pour ce faire, vous avez besoin de l'adresse de l'objet sur tas, et la taille de l'objet

  • définir un Object pour pointer vers la mémoire allouée, puis cast le Object à votre classe.

    Il ne semble pas possible de définir directement l'adresse d'une variable avec Unsafe. Nous devons donc placer l'objet dans un objet tableau ou wrapper et utiliser Unsafe#arrayBaseOffset ou Unsafe#objectFieldOffset.

  • une fois que vous avez terminé, libérer la mémoire allouée avec freeMemory

Si jamais je reçois ce ne pas je vais poster un segmentation fault un exemple :-)

ByteBuffer

Avantages par rapport aux risques:

  • stable dans les versions Java tout en compromettant la sécurité peuvent briser
  • ne lié vérification, donc plus sûr que ... non sécurisée, ce qui permet de fuites de mémoire et SIGSEGV

JLS says:

Le contenu des tampons directs peut résider en dehors du tas normal ramassé par les ordures.

Exemple d'utilisation des primitives:

ByteBuffer bb = ByteBuffer.allocateDirect(8); 

bb.putInt(0, 1); 
bb.putInt(4, 2); 
assert bb.getInt(0) == 1; 
assert bb.getInt(4) == 2; 

// Bound chekcs are done. 
boolean fail = false; 
try { 
    bb.getInt(8); 
} catch(IndexOutOfBoundsException e) { 
    fail = true; 
} 
assert fail; 

discussions connexes:

1

Je me demande si ce qu'ils vont pour le modèle avec des pools de ressources (par exemple. pour les connexions réseau/db, ou threads) où vous utilisez finalize pour renvoyer une ressource au pool afin que l'objet réel contenant la ressource ne soit pas GC'ed.

exemple stupide, dans pseudocode de type Java et manquant tout type de synchronisation:

class SlowResourceInternal { 
    private final SlowResourcePool parent; 
    <some instance data> 

    returnToPool() { 
     parent.add(this); 
    } 
} 

class SlowResourceHolder { 
    private final SlowResourceInternal impl; 

    <delegate actual stuff to the internal object> 

    finalize() { 
     if (impl != null) impl.returnToPool(); 
    } 
} 
Questions connexes