Selon Effective Java (Deuxième édition) de Joshua Bloch, deux scénarios finalize()
sont utiles lorsque :
L'une consiste à agir en tant que «filet de sécurité» au cas où le propriétaire d'un objet oublie d'appeler sa méthode de terminaison explicite. Bien que rien ne garantisse que le finaliseur sera appelé rapidement, il peut être préférable de libérer la ressource plus tard que jamais, dans les cas (espérons-le rares) où le client ne parvient pas à appeler la méthode de terminaison explicite. Mais le finaliseur doit enregistrer un avertissement s’il constate que la ressource n’a pas été arrêtée.
Une deuxième utilisation légitime des finaliseurs concerne les objets avec des pairs natifs. Un homologue natif est un objet natif auquel un objet normal délègue via des méthodes natives. Etant donné qu'un homologue natif n'est pas un objet normal, le récupérateur de mémoire ne le connaît pas et ne peut pas le récupérer lorsque son homologue Java est récupéré. Un finaliseur est un véhicule approprié pour effectuer cette tâche, en supposant que l'homologue natif ne possède aucune ressource critique. Si le pair natif contient des ressources qui doivent être terminées rapidement, la classe doit avoir une méthode de terminaison explicite, comme décrit ci-dessus. La méthode de terminaison doit faire le nécessaire pour libérer la ressource critique.
Pour en savoir plus, reportez-vous à la rubrique 7, page 27.
Les finaliseurs sont importants pour la gestion des ressources natives. Par exemple, votre objet peut avoir besoin d'allouer un WidgetHandle à partir du système d'exploitation à l'aide d'une API non Java. Si vous ne relâchez pas ce WidgetHandle lorsque votre objet est défini sur GC'd, vous allez laisser filtrer WidgetHandles.
Ce qui est important, c'est que le cas "du finaliseur ne s'appelle jamais" se décompose plutôt simplement:
Dans ces trois cas, vous n’avez pas de fuite native (du fait que votre programme ne fonctionne plus), ou vous avez déjà une fuite non native (si vous continuez à allouer des objets gérés sans les GC'd).
L'avertissement "ne comptez pas sur le finaliseur étant appelé" est en réalité sur le fait de ne pas utiliser les finaliseurs pour la logique de programme. Par exemple, vous ne voulez pas savoir combien de vos objets existent dans toutes les instances de votre programme en incrémentant un compteur dans un fichier quelque part pendant la construction et en le décrémentant dans un finaliseur - car rien ne garantit que vos objets être finalisé, ce compteur de fichiers ne reviendra probablement jamais à 0. C’est là un cas particulier du principe plus général qui veut que vous ne dépendiez pas de la fin normale de votre programme (pannes de courant, etc.).
Pour la gestion des ressources natives, cependant, les cas où le finaliseur ne s'exécute pas correspondent aux cas où vous ne vous souciez pas de son exécution.
la source
close()
n'est jamais appelé avant qu'il ne soit inaccessible)Le but de cette méthode est expliqué dans la documentation de l'API comme suit:
Si vous êtes également intéressé par les raisons pour lesquelles les concepteurs de langage ont choisi cet "objet est irrévocablement jeté" ( ordures collectées ) d'une manière qui échappe au contrôle du programmeur d'application ("nous ne devrions jamais nous fier"), cela a été expliqué dans une réponse à question :
La citation ci-dessus, à son tour, a été extraite de la documentation officielle sur les objectifs de conception Java , c’est-à-dire qu’elle peut être considérée comme une référence faisant autorité qui explique pourquoi les concepteurs de langage Java ont pris cette décision.
Pour une discussion plus détaillée et agnostique de la langue de cette préférence, référez- vous à la section 9.6 de l' OOSC Gestion automatique de la mémoire (en fait, non seulement cette section, mais tout le chapitre 9 vaut vraiment la peine d'être lu si vous vous intéressez à ce genre de choses). Cette section s'ouvre par une déclaration sans ambiguïté:
la source
Les finaliseurs existent parce qu’ils étaient censés être un moyen efficace d’assurer le nettoyage des objets (même s’ils ne le sont pas en pratique), et parce que, lorsqu’ils ont été inventés, de meilleurs moyens d’assurer le nettoyage (tels que les références fantômes et les tests d’essai). ressources) n’existait pas encore. Avec le recul, Java serait probablement mieux si les efforts consacrés à la mise en œuvre de sa fonctionnalité "finalize" avaient été consacrés à d'autres moyens de nettoyage, mais cela n'était guère clair au moment du développement initial de Java.
la source
CAVEAT: Je suis peut-être obsolète, mais c'est ce que j'avais compris il y a quelques années:
En général, rien ne garantit le moment où un finaliseur est exécuté - ou même son exécution, bien que certaines machines virtuelles vous permettent de demander un GC complet et la finalisation avant la fin du programme (ce qui signifie bien sûr que le programme prend plus de temps. quitter, et qui n’est pas le mode de fonctionnement par défaut).
Et certains GC étaient connus pour retarder ou éviter explicitement les objets GC ayant des finaliseurs, dans l’espoir que cela produirait de meilleures performances sur les points de repère.
Ces comportements sont malheureusement en conflit avec les raisons initiales recommandées par les finaliseurs et ont plutôt encouragé l'utilisation de méthodes d'arrêt explicitement appelées.
Si vous avez vraiment un objet à nettoyer avant de le jeter, et si vous ne pouvez vraiment pas faire confiance aux utilisateurs, un finaliseur peut toujours être utile. Mais en règle générale, il existe de bonnes raisons pour lesquelles vous ne les voyez pas aussi souvent dans le code Java moderne que dans certains des premiers exemples.
la source
Le Guide de style Java de Google contient quelques conseils avisés sur le sujet:
la source
PhantomReference
et à laReferenceQueue
place.PhantomReference
c'est une meilleure solution. Les finaliseurs sont une verrue des débuts de Java et, commeObject.clone()
les types bruts, font partie du langage le mieux oublié.La spécification du langage Java (Java SE 7) indique:
la source