2010-03-15 7 views
9

Ma compréhension de la finalisation est la suivante:Quel est le but de la finalisation en Java?

Pour nettoyer ou récupérer la mémoire occupée par un objet, le récupérateur de place entre en action. (automatiquement invoqué?)

Le garbage collector déserère ensuite l'objet. Parfois, il n'y a aucun moyen pour le garbage collector d'accéder à l'objet. Ensuite, finalize est appelé pour effectuer un nettoyage final, après quoi le garbage collector peut être appelé.

Est-ce une description précise de la finalisation?

Répondre

15

Le garbage collector fonctionne automatiquement en arrière-plan (bien qu'il puisse être explicitement appelé, mais la nécessité pour cela devrait être rare). Il ne nettoie essentiellement que les objets qui ne sont pas référencés par d'autres objets (à condition que l'image complète soit plus compliquée, mais c'est l'idée de base). Cela ne change donc aucune référence dans les objets live. Si un objet n'est pas accessible à partir d'un objet en direct, cela signifie qu'il peut être collecté en toute sécurité.

La finalisation était destinée à nettoyer les ressources acquises par l'objet (pas la mémoire, mais d'autres ressources, par exemple les poignées de fichiers, les ports, les connexions DB, etc.). Cependant, ça n'a pas vraiment :-(

  • il est imprévisible quand finalize() sera appelé
  • en fait, il n'y a aucune garantie que finalize() sera appelée jamais!

Ainsi, même Si l'appel était garanti, ce ne serait pas un bon endroit pour libérer des ressources: au moment où il est appelé pour libérer toutes les connexions DB que vous avez ouvertes, le système a peut-être complètement épuisé les connexions gratuites et votre application ne fonctionne plus

6

Non. La méthode finalize() est exécutée uniquement si le garbage collector tente de récupérer votre objet.

Toute mémoire utilisée par votre objet (généralement, je ne peux pas penser à une exception) sera automatiquement connectée à votre objet et nettoyée avec elle. La finalisation n'est donc pas destinée à libérer la mémoire, mais plutôt toutes les autres ressources auxquelles votre objet peut être associé. Par exemple, cela pourrait être utilisé pour fermer des fichiers ouverts ou des connexions de base de données, ou peut-être exécuter du code de bas niveau en interface avec le système d'exploitation pour libérer des ressources au niveau du système.

+1

Vous ne pouvez pas dire « si et seulement si »; vous pouvez dire "seulement si", mais il n'y a aucune garantie que ce sera appelé du tout. –

+0

@mmyers: +1. récupéré -> finalize a été exécuté, mais pas l'inverse. – danben

+0

Il n'y a pas de "mais" là: Si le GC tente de récupérer la mémoire, finalize() est appelée. –

7

De this article:

Les instances de classes qui mettent en œuvre la méthode finalize() sont souvent appelés des objets finalisables. Ils ne seront pas immédiatement récupérés par le garbage collector Java lorsqu'ils ne sont plus référencés. Au lieu de cela, le garbage collector Java ajoute les objets à une file d'attente spéciale pour le processus de finalisation . Habituellement c'est effectué par un fil spécial appelé un "gestionnaire de référence" sur certaines machines virtuelles Java . Au cours de ce processus de finalisation , le thread "Finalizer" exécutera chaque méthode finalize() des objets.Ce n'est qu'après la réussite de la méthode finalize() qu'un objet sera remis à la poubelle Java pour obtenir son espace récupéré par "future" garbage collection. Vous pouvez faire pratiquement n'importe quoi dans la méthode finalize() de votre classe . Lorsque vous faites cela, s'il vous plaît ne pas attendre l'espace de mémoire occupé par chaque objet à être récupéré par le garbage collector Java lorsque l'objet n'est plus référencé ou pas plus longtemps nécessaire. Pourquoi? Il n'est pas garanti que la méthode finalize() terminera l'exécution en temps opportun . Dans le pire des cas, il peut ne pas être invoqué même s'il n'y a plus de références à l'objet. Cela signifie il n'est pas garanti que tous les objets qui ont une méthode finalize() sont ordures collectées.

En outre, this article de Sun a quelques bons diagrammes expliquant le processus.

5

En fait, voici le comportement du finalize() méthode:

Une fois les pistes de Garbage Collector (la machine virtuelle décide qu'il a besoin de libérer de la mémoire, vous ne pouvez pas le forcer à exécuter) et a décidé de recueillir la mémoire de cet objet (ce qui signifie qu'il n'y a AUCUNE référence pointant vers lui, à partir des objets accessibles au moins), juste avant de supprimer la mémoire qu'il occupe, il exécute la méthode finalize() sur l'objet. Vous pouvez être sûr que si le garbage est collecté, l'objet exécutera finalize() juste avant qu'il ne disparaisse, mais vous ne pouvez pas être sûr qu'il sera du tout GC'ed donc vous ne devriez pas vous fier à la méthode pour effectuer une désinfection du tout . Vous devez exécuter des instructions de nettoyage à l'intérieur des blocs finally {} et ne pas utiliser finalize() car il n'est pas garanti de s'exécuter.

En outre, certaines personnes ont effectué des tests de performance et ont montré que la méthode de finalisation ralentissait quelque peu la création/destruction de l'objet. Je ne peux pas me souvenir de la source, donc traiter cette information comme n'étant pas très fiable. :)

+0

Les finaliseurs ont causé des problèmes de performances dans NIO. Ils ont été supprimés en 1.4.1 (ou éventuellement en 1.4.2). Si vous utilisez NIO, vous devez savoir comment utiliser 'finally' correctement. –

2

La finalisation est utilisée pour nettoyer les ressources qui ne peuvent pas être libérées par le garbage collector. Par exemple, considérons un programme qui alloue (via certaines ressources native API) directement à partir du système d'exploitation. Cela donne généralement une sorte de « poignée » (un descripteur de fichier UNIX ou Windows MANCHE, ou quelque chose de similaire):

class Wrapper { 
    private long handle; 

    private Handle(long h) { 
     handle = h; 
    } 

    private static native long getHandleFromOS(); 

    static Wrapper allocate() { 
     return new Handle(getHandleFromOS()); 
    } 
} 

Alors, ce qui se passe, si votre code attribue une instance de classe Wrapper? Eh bien, la classe alloue une sorte de ressource spécifique au système d'exploitation et conserve une référence à celle-ci (le handle) dans une variable membre. Mais que se passe-t-il lorsque la dernière référence Java à une instance de wrapper est perdue? Maintenant, le garbage collector récupérera (à un moment donné) l'espace de l'instance de wrapper maintenant défunte. Mais qu'advient-il de la ressource du système d'exploitation allouée par le wrapper? Il sera divulgué dans le scénario ci-dessus, ce qui est une mauvaise chose, si c'est une ressource coûteuse, comme un descripteur de fichier. Pour permettre à votre code de nettoyer dans un tel scénario, il existe la méthode finalize.

class Wrapper { 
    private long handle; 

    private Handle(long h) { 
     handle = h; 
    } 

    protected void finalize() { 
     returnHandleToOS(handle); 
    } 

    private static native long getHandleFromOS(); 
    private static native void returnHandleToOS(long handle); 

    static Wrapper allocate() { 
     return new Handle(getHandleFromOS()); 
    } 
} 

Maintenant, lorsque le GC récupère l'espace d'une instance d'emballage, le finaliseur fait en sorte que la ressource est correctement retournée au système d'exploitation.

Cela semble très bien, mais comme d'autres l'ont déjà souligné, l'inconvénient est que la finalisation est intrinsèquement peu fiable: vous ne savez pas quand le finalizer sera exécuté. Pire: il n'y a aucune garantie qu'il sera exécuté du tout. Alors ist mieux pour fournir un mécanisme dispose et utiliser la finalisation seulement comme filet de sécurité en cas, les clients de votre classe oublient de disposer correctement leurs références:

class Wrapper { 
    private long handle; 

    private Handle(long h) { 
     handle = h; 
    } 

    protected void finalize() { 
     if(handle != 0) returnHandleToOS(handle); 
    } 

    public void dispose() { 
     returnHandleToOS(handle); 
     handle = 0; 
    } 

    private static native long getHandleFromOS(); 
    private static native void returnHandleToOS(long handle); 

    static Wrapper allocate() { 
     return new Handle(getHandleFromOS()); 
    } 
}