J'ai lu beaucoup de questions sur les recrues Java finalize()
et je trouve déconcertant que personne n'ait vraiment expliqué clairement que finalize () est un moyen peu fiable de nettoyer les ressources. J'ai vu quelqu'un commenter qu'il l'utilisait pour nettoyer les connexions, ce qui est vraiment effrayant car la seule façon de se rapprocher le plus d'une garantie de fermeture d'une connexion est d'implémenter finalement try (catch).
Je n'étais pas scolarisé en CS, mais je programme en Java professionnellement depuis près d'une décennie maintenant et je n'ai jamais vu personne implémenter finalize()
dans un système de production. Cela ne signifie toujours pas qu'il n'a pas ses utilisations ou que les gens avec qui j'ai travaillé l'ont bien fait.
Donc ma question est, quels sont les cas d'utilisation pour l'implémentation finalize()
qui ne peuvent pas être traités de manière plus fiable via un autre processus ou syntaxe dans le langage?
Veuillez fournir des scénarios spécifiques ou votre expérience, simplement répéter un manuel Java ou finaliser l'utilisation prévue n'est pas suffisant, comme ce n'est pas le but de cette question.
finalize()
. Cependant, le code de bibliothèque de plate - forme , tel queSocketInputStream
, qui gère les ressources natives au nom de l'appelant, le fait pour essayer de minimiser le risque de fuites de ressources (ou utilise des mécanismes équivalents, tels quePhantomReference
, qui ont été ajoutés plus tard.) Donc, l'écosystème en a besoin , même si 99,9999% des développeurs n'en écriront jamais.Réponses:
Vous pouvez l'utiliser comme backstop pour un objet contenant une ressource externe (socket, fichier, etc.). Implémentez une
close()
méthode et documentez-la.Implémentez
finalize()
pour effectuer leclose()
traitement si vous détectez que cela n'a pas été fait. Peut-être avec quelque chose sous-jacent pourstderr
souligner que vous nettoyez après un appelant buggy.Il offre une sécurité supplémentaire dans une situation exceptionnelle / buggy. Tous les appelants ne feront pas le bon travail à
try {} finally {}
chaque fois. Malheureux, mais vrai dans la plupart des environnements.Je suis d'accord que c'est rarement nécessaire. Et comme le soulignent les commentateurs, il est livré avec des frais généraux GC. Utilisez uniquement si vous avez besoin de la sécurité «ceinture et bretelles» dans une application de longue durée.
Je vois qu'à partir de Java 9,
Object.finalize()
c'est obsolète! Ils nous indiquentjava.lang.ref.Cleaner
etjava.lang.ref.PhantomReference
comme alternatives.la source
finalize()
est un indice pour la JVM qu'il peut être intéressant d'exécuter votre code à un moment non spécifié. C'est une bonne chose lorsque vous voulez que le code échoue mystérieusement.Faire quoi que ce soit d'important dans les finaliseurs (essentiellement tout sauf la journalisation) est également bon dans trois situations:
Si vous pensez que vous avez besoin de finalize (), parfois ce que vous voulez vraiment est une référence fantôme (qui dans l'exemple donné pourrait contenir une référence dure à une connexion utilisée par son referand, et la fermer après que la référence fantôme a été mise en file d'attente). Cela a également la propriété de ne jamais s'exécuter mystérieusement, mais au moins, il ne peut pas appeler de méthodes ou ressusciter des objets finalisés. Donc, c'est juste pour les situations où vous n'avez pas absolument besoin de fermer cette connexion proprement, mais vous le souhaitez, et les clients de votre classe ne peuvent pas ou ne veulent pas appeler close eux-mêmes (ce qui est en fait assez juste - quoi' s l'intérêt d'avoir un garbage collector si vous concevez des interfaces qui nécessitent une action spécifique avant la collecte? Cela nous ramène au temps de malloc / free.)
D'autres fois, vous avez besoin de la ressource que vous pensez pouvoir gérer pour être plus robuste. Par exemple, pourquoi avez-vous besoin de fermer cette connexion? Il doit finalement être basé sur une sorte d'E / S fournies par le système (socket, fichier, etc.), alors pourquoi ne pouvez-vous pas compter sur le système pour le fermer pour vous lorsque le niveau de ressource le plus bas est obtenu? Si le serveur à l'autre extrémité vous oblige absolument à fermer la connexion proprement plutôt que de simplement laisser tomber la prise, alors que va-t-il se passer quand quelqu'un trébuche sur le câble d'alimentation de la machine sur laquelle votre code fonctionne, ou que le réseau intervenant s'éteint?
Avertissement: J'ai travaillé sur une implémentation JVM dans le passé. Je déteste les finaliseurs.
la source
finalize()
sans donner de raisons à quelqu'un de vouloir l'utiliserjava.lang.ref.Reference
et ses sous-classes. Java 1 avait des variables :-) Et oui, il y avait aussi des finaliseurs.Une règle simple: ne jamais utiliser de finaliseurs.
Le seul fait qu'un objet possède un finaliseur (quel que soit le code qu'il exécute) est suffisant pour entraîner une surcharge considérable pour la collecte des ordures.
Extrait d'un article de Brian Goetz:
la source
La seule fois où j'ai utilisé finaliser dans le code de production était d'implémenter une vérification que les ressources d'un objet donné avaient été nettoyées, et sinon, d'enregistrer un message très vocal. Il n'a pas vraiment essayé de le faire lui-même, il a juste crié beaucoup s'il n'était pas fait correctement. S'est avéré très utile.
la source
Je fais du Java professionnellement depuis 1998 et je ne l'ai jamais implémenté
finalize()
. Pas une fois.la source
La réponse acceptée est bonne, je voulais juste ajouter qu'il y a maintenant un moyen d'avoir la fonctionnalité de finaliser sans vraiment l'utiliser du tout.
Regardez les classes "Référence". Référence faible, référence fantôme et référence souple.
Vous pouvez les utiliser pour garder une référence à tous vos objets, mais cette référence SEULE n'arrêtera pas GC. La chose intéressante à ce sujet est que vous pouvez le faire appeler une méthode quand il sera supprimé, et cette méthode peut être garantie d'être appelée.
Quant à finaliser: j'ai utilisé finaliser une fois pour comprendre quels objets étaient libérés. Vous pouvez jouer à des jeux soignés avec des statistiques, le comptage des références et autres - mais c'était uniquement pour l'analyse, mais faites attention au code comme celui-ci (pas seulement dans la finalisation, mais c'est là que vous êtes le plus susceptible de le voir):
C'est un signe que quelqu'un ne savait pas ce qu'il faisait. Un "nettoyage" comme celui-ci n'est pratiquement jamais nécessaire. Lorsque la classe est GC'd, cela se fait automatiquement.
Si vous trouvez du code comme celui-ci dans une version finale, il est garanti que la personne qui l'a écrit est confuse.
Si c'est ailleurs, il se peut que le code soit un correctif valide pour un mauvais modèle (une classe reste longtemps et pour une raison quelconque, les choses auxquelles elle fait référence doivent être libérées manuellement avant que l'objet ne soit GC). Généralement, c'est parce que quelqu'un a oublié de supprimer un auditeur ou quelque chose et ne peut pas comprendre pourquoi son objet n'est pas GC, il supprime simplement les choses auxquelles il fait référence, hausse les épaules et s'en va.
Il ne devrait jamais être utilisé pour nettoyer les choses "plus rapidement".
la source
Je ne sais pas ce que vous pouvez en faire, mais ...
Je suppose donc que le Soleil a trouvé des cas où (ils pensent) qu'il devrait être utilisé.
la source
finalize
plutôt que de l'utiliser réellement?$FAVORITE_IDE
.====================================
résultat:
=====================================
Vous pouvez donc rendre une instance inaccessible accessible dans la méthode finalize.
la source
System.gc();
n'est pas une garantie que le ramasse-miettes fonctionnera réellement. Il est à l'entière discrétion du GC de nettoyer le tas (peut-être encore assez de tas libre, peut-être pas trop fragmenté, ...). Ce n'est donc qu'une humble demande du programmeur "s'il vous plaît, pourriez-vous m'écouter et courir?" et pas une commande "exécuter maintenant comme je dis!". Désolé d'utiliser des mots linguaux, mais cela indique exactement comment fonctionne le GC de la JVM.finalize()
peut être utile pour détecter les fuites de ressources. Si la ressource doit être fermée mais n'écrit pas le fait qu'elle n'a pas été fermée dans un fichier journal et fermez-le. De cette façon, vous supprimez la fuite de ressources et vous donnez un moyen de savoir qu'elle s'est produite afin que vous puissiez la corriger.Je programme en Java depuis la version 1.0 alpha 3 (1995) et je n'ai pas encore annulé la finalisation pour quoi que ce soit ...
la source
Vous ne devez pas dépendre de finalize () pour nettoyer vos ressources à votre place. finalize () ne fonctionnera pas tant que la classe n'aura pas été récupérée, si c'est le cas. Il est préférable de libérer explicitement les ressources lorsque vous avez fini de les utiliser.
la source
Pour mettre en évidence un point dans les réponses ci-dessus: les finaliseurs seront exécutés sur le seul thread GC. J'ai entendu parler d'une démo majeure de Sun où les développeurs ont ajouté un petit sommeil à certains finaliseurs et ont intentionnellement mis à genoux une démo 3D autrement fantaisiste.
Mieux vaut éviter, à l'exception possible des diagnostics test-env.
Eckel's Thinking in Java contient une bonne section à ce sujet.
la source
Hmmm, je l'ai utilisé une fois pour nettoyer des objets qui n'étaient pas retournés dans un pool existant.
Ils ont été beaucoup circulés, il était donc impossible de dire quand ils pouvaient être ramenés en toute sécurité à la piscine. Le problème était qu'il introduisait une énorme pénalité lors de la collecte des ordures qui était bien plus importante que toute économie de mise en commun des objets. Il était en production depuis environ un mois avant que je ne déchire toute la piscine, que tout ne soit dynamique et que j'en ai fini avec.
la source
Faites attention à ce que vous faites dans un
finalize()
. Surtout si vous l'utilisez pour des choses comme appeler close () pour vous assurer que les ressources sont nettoyées. Nous avons rencontré plusieurs situations où nous avions des bibliothèques JNI liées au code java en cours d'exécution et dans toutes les circonstances où nous avons utilisé finalize () pour appeler des méthodes JNI, nous obtenions une très mauvaise corruption de tas java. La corruption n'a pas été causée par le code JNI sous-jacent lui-même, toutes les traces de mémoire étaient correctes dans les bibliothèques natives. C'était juste le fait que nous appelions des méthodes JNI à partir de finalize ().C'était avec un JDK 1.5 qui est encore largement utilisé.
Nous ne découvririons que quelque chose s'est mal passé que beaucoup plus tard, mais au final, le coupable était toujours la méthode finalize () utilisant les appels JNI.
la source
Lors de l'écriture de code qui sera utilisé par d'autres développeurs qui nécessite une sorte de méthode de «nettoyage» pour être libéré afin de libérer des ressources. Parfois, ces autres développeurs oublient d'appeler votre méthode de nettoyage (ou de fermer, de détruire ou quoi que ce soit). Pour éviter d'éventuelles fuites de ressources, vous pouvez archiver la méthode finalize pour vous assurer que la méthode a été appelée et si ce n'est pas le cas, vous pouvez l'appeler vous-même.
De nombreux pilotes de base de données le font dans leurs implémentations Statement et Connection pour fournir un peu de sécurité contre les développeurs qui oublient d'appeler de près.
la source
Edit: D'accord, cela ne fonctionne vraiment pas. Je l'ai implémenté et j'ai pensé que si cela échouait parfois, cela me convenait, mais il n'a même pas appelé la méthode finalize une seule fois.
Je ne suis pas un programmeur professionnel mais dans mon programme, j'ai un cas qui, à mon avis, est un bon exemple d'utilisation de finalize (), c'est-à-dire un cache qui écrit son contenu sur le disque avant qu'il ne soit détruit. Parce qu'il n'est pas nécessaire qu'il soit exécuté à chaque fois sur la destruction, cela ne fait qu'accélérer mon programme, j'espère que je ne l'ai pas mal fait.
la source
Il peut être utile de supprimer des éléments qui ont été ajoutés à un emplacement global / statique (par nécessité) et doivent être supprimés lorsque l'objet est supprimé. Par exemple:
la source
this
instance qui lui est transmise dans le constructeur, cette instance ne deviendra jamais inaccessible, donc lefinalize()
nettoyage ne sera jamais nettoyé de toute façon. Si, en revanche, l'auditeur détient une référence faible à cet objet, comme son nom l'indique, cette construction risque d'être nettoyée à des moments où elle ne devrait pas. Les cycles de vie des objets ne sont pas le bon outil pour implémenter la sémantique des applications.iirc - vous pouvez utiliser la méthode de finalisation comme un moyen d'implémenter un mécanisme de mise en commun pour les ressources coûteuses - afin qu'ils n'obtiennent pas trop de GC.
la source
En note:
Source: finalize () déconseillé sur java-9
la source
Les ressources (fichier, socket, flux, etc.) doivent être fermées une fois que nous en avons fini avec elles. Ils ont généralement une
close()
méthode que nous appelons généralement dans lafinally
section destry-catch
déclarations. quelquefoisfinalize()
certains développeurs peuvent également utiliser IMO, mais ce n'est pas un moyen approprié car il n'y a aucune garantie que finalize sera toujours appelé.Dans Java 7, nous avons une instruction try-with-resources qui peut être utilisée comme:
Dans l'exemple ci-dessus, try-with-resource fermera automatiquement la ressource
BufferedReader
en appelant laclose()
méthode. Si nous voulons, nous pouvons également implémenter Closeable dans nos propres classes et l'utiliser de la même manière. OMI, il semble plus soigné et simple à comprendre.la source
Personnellement, je n'ai presque jamais utilisé
finalize()
sauf dans une rare circonstance: j'ai fait une collection personnalisée de type générique et j'ai écrit unefinalize()
méthode personnalisée qui fait ce qui suit:(
CompleteObject
Est une interface I fait qui vous permet de spécifier que vous avez mis rarement mis en œuvre desObject
méthodes telles que#finalize()
,#hashCode()
et#clone()
)Ainsi, en utilisant une
#setDestructivelyFinalizes(boolean)
méthode sœur , le programme utilisant ma collection peut (aider) garantir que la destruction d'une référence à cette collection détruit également les références à son contenu et supprime toutes les fenêtres qui pourraient maintenir la JVM en vie involontairement. J'ai également envisagé d'arrêter tous les threads, mais cela a ouvert une toute nouvelle boîte de vers.la source
finalize()
à vosCompleteObject
instances comme vous le faites dans le code ci-dessus implique qu'il pourrait être appelé deux fois, car la JVM pourrait toujours invoquerfinalize()
une fois que ces objets sont devenus inaccessibles, ce qui, en raison de la logique de finalisation, pourrait être avant lafinalize()
méthode de votre collection spéciale est appelé ou même en même temps. Comme je ne vois aucune mesure pour assurer la sécurité des threads dans votre méthode, vous semblez ne pas en être conscient…La liste des réponses acceptées indique que la fermeture d'une ressource pendant la finalisation peut être effectuée.
Cependant, cette réponse montre qu'au moins dans java8 avec le compilateur JIT, vous rencontrez des problèmes inattendus où parfois le finaliseur est appelé avant même de terminer la lecture d'un flux géré par votre objet.
Donc, même dans cette situation, appeler finaliser ne serait pas recommandé.
la source