Y a-t-il un destructeur pour Java?

Y a-t-il un destructeur pour Java? Je ne semble pas pouvoir trouver de documentation à ce sujet. S’il n’y en a pas, comment puis-je obtenir le même effet?

Pour rendre ma question plus précise, j’écris une application qui traite des données et des spécifications, indiquant qu’il devrait y avoir un bouton de réinitialisation qui ramène l’application à son état d’origine juste lancé. Cependant, toutes les données doivent être «actives» à moins que l’application ne soit fermée ou que le bouton de réinitialisation ne soit activé.

Étant généralement un programmeur C / C ++, je pensais que cela serait sortingvial à mettre en œuvre. (Et donc j’ai prévu de l’implémenter en dernier.) J’ai structuré mon programme de telle sorte que tous les objects «réinitialisables» soient dans la même classe afin que je puisse détruire tous les objects «live» lorsqu’un bouton de réinitialisation est pressé.

Je pensais que si tout ce que je faisais était juste de déréférencer les données et d’attendre que le ramasse-miette les collecte, n’y aurait-il pas une fuite de mémoire si mon utilisateur saisissait des données à plusieurs resockets et appuyait sur le bouton de réinitialisation? Je pensais également que Java étant un langage assez mature, il devrait y avoir un moyen d’empêcher que cela ne se produise ou de s’attaquer à ce problème avec grâce.

Comme Java est un langage récupéré, vous ne pouvez pas prédire quand (ou même si) un object sera détruit. Il n’y a donc pas d’équivalent direct d’un destructeur.

Il existe une méthode héritée appelée finalize , mais elle est entièrement appelée à la discrétion du ramasse-miettes. Donc, pour les classes qui ont besoin de nettoyer explicitement, la convention est de définir une méthode close et d’utiliser Finalize uniquement pour vérifier la validité (si close n’a pas été appelée, faites-le maintenant et enregistrez une erreur).

Il y a eu une question qui a suscité une discussion approfondie sur la finalisation récemment, de sorte que cela devrait fournir plus de profondeur si nécessaire …

Non, pas de destructeurs ici. La raison en est que tous les objects Java sont alloués au tas et collectés. Sans désallocation explicite (c.-à-d. L’opérateur de suppression de C ++), il n’y a pas de moyen sensé d’implémenter de vrais destructeurs.

Java prend en charge les finaliseurs, mais ils sont conçus pour être utilisés uniquement comme protection pour les objects contenant un descripteur de ressources natives telles que les sockets, les descripteurs de fichiers, les descripteurs de fenêtre, etc. Lorsque le ramasse-miettes collecte un object sans finaliseur région comme libre et c’est tout. Lorsque l’object a un finaliseur, il est d’abord copié dans un emplacement temporaire (souvenez-vous, nous collectons des erreurs ici), puis il est placé dans une queue en attente de finalisation, puis un thread Finalizer interroge la queue avec une très faible priorité et exécute le finaliseur.

Lorsque l’application se ferme, la machine virtuelle Java s’arrête sans attendre que les objects en attente soient finalisés. Il n’y a donc pratiquement aucune garantie que vos finaliseurs s’exécutent.

Si vous utilisez Java 7, consultez l’instruction try-with-resources . Par exemple:

 try (BufferedReader br = new BufferedReader(new FileReader(path))) { System.out.println(br.readLine()); } catch (Exception e) { ... } finally { ... } 

Ici, la ressource qui n’est plus nécessaire est libérée dans la méthode BufferedReader.close() . Vous pouvez créer votre propre classe qui implémente AutoCloseable et l’utiliser de la même manière.

Cette déclaration est plus limitée que la finalize en termes de structuration du code, mais en même temps, elle simplifie la compréhension et la maintenance du code. De plus, il n’y a aucune garantie qu’une méthode de finalize soit appelée pendant la durée de vie de l’application.

L’utilisation des méthodes finalize () doit être évitée. Ils ne sont pas un mécanisme fiable pour le nettoyage des ressources et il est possible de provoquer des problèmes dans le ramasse-miettes en les abusant.

Si vous avez besoin d’un appel à la désallocation dans votre object, par exemple pour libérer des ressources, utilisez un appel de méthode explicite. Cette convention est visible dans les API existantes (par exemple, Closeable , Graphics.dispose () , Widget.dispose () ) et est généralement appelée via try / finally.

 Resource r = new Resource(); try { //work } finally { r.dispose(); } 

Les tentatives d’utilisation d’un object éliminé doivent déclencher une exception d’exécution (voir IllegalStateException ).


MODIFIER:

Je pensais que si tout ce que je faisais consistait simplement à déréférencer les données et à attendre que le ramasse-miette les collecte, n’y aurait-il pas de fuite de mémoire si mon utilisateur saisissait des données à plusieurs resockets?

En général, tout ce que vous devez faire est de déréférencer les objects – du moins, c’est la façon dont cela est censé fonctionner. Si vous êtes inquiet à propos de la récupération de place, consultez la section Optimisation du ramasse-miettes pour machines virtuelles Java SE 6 HotSpot [tm] (ou le document correspondant à votre version de JVM).

Avec Java 1.7 publié, vous avez maintenant l’option supplémentaire d’utiliser le bloc try-with-resources . Par exemple,

 public class Closeable implements AutoCloseable { @Override public void close() { System.out.println("closing..."); } public static void main(Ssortingng[] args) { try (Closeable c = new Closeable()) { System.out.println("trying..."); throw new Exception("throwing..."); } catch (Exception e) { System.out.println("catching..."); } finally { System.out.println("finalizing..."); } } } 

Si vous exécutez cette classe, c.close() sera exécuté lorsque le bloc try sera laissé et avant que les blocs catch et finally soient exécutés. Contrairement à la méthode finalize() , close() est garanti pour être exécuté. Cependant, il n’est pas nécessaire de l’exécuter explicitement dans la clause finally .

Je suis entièrement d’accord avec d’autres réponses, en disant de ne pas compter sur l’exécution de la finalisation.

En plus des blocs try-catch-finally, vous pouvez utiliser Runtime # addShutdownHook (introduit dans Java 1.6) pour effectuer des nettoyages finaux dans votre programme.

Ce n’est pas la même chose que les destructeurs , mais on peut implémenter un hook d’arrêt avec des objects écouteurs enregistrés sur lesquels les méthodes de nettoyage (connexions de bases persistantes, supprimer les verrous de fichiers, etc.) peuvent être invoquées. destructeurs . Encore une fois – ce n’est pas un remplacement pour les constructeurs mais dans certains cas, vous pouvez aborder la fonctionnalité souhaitée avec cela.

L’avantage de ceci est d’avoir un comportement de déconstruction faiblement couplé au rest de votre programme.

Non, java.lang.Object#finalize est le plus proche possible.

Cependant, quand (et si) il est appelé, n’est pas garanti.
Voir: java.lang.Runtime#runFinalizersOnExit(boolean)

Tout d’abord, notez que Java n’est pas récupéré, il est rare de devoir faire quelque chose pour détruire des objects. D’abord parce que vous n’avez généralement pas de ressources gérées à libérer, et deuxièmement parce que vous ne pouvez pas prédire quand ou si cela se produira, il est donc inapproprié de faire les choses “dès que personne n’utilise plus mon object “.

Vous pouvez être averti lorsqu’un object a été détruit à l’aide de java.lang.ref.PhantomReference (en fait, dire qu’il a été détruit peut être légèrement inexact, mais si une référence fantôme est mise en queue, il n’est plus récupérable, ce qui revient généralement à la même chose). Un usage courant est:

  • Séparez les ressources de votre classe qui doivent être détruites dans un autre object d’assistance (notez que si vous fermez une connexion, ce qui est un cas courant, vous n’avez pas besoin d’écrire une nouvelle classe: la connexion à fermer serait “l’object d’assistance” dans ce cas).
  • Lorsque vous créez votre object principal, créez également une référence fantôme. Soit cela fait référence au nouvel object assistant, soit configure une carte à partir des objects PhantomReference vers leurs objects auxiliaires correspondants.
  • Une fois l’object principal collecté, la PhantomReference est mise en queue (ou plutôt peut être mise en queue). Comme les finaliseurs, rien ne garantit que cela se produira, par exemple si la machine virtuelle se ferme, elle n’attendra pas. Assurez-vous de traiter sa queue (dans un thread spécial ou de temps en temps). En raison de la référence difficile à l’object d’assistance, l’object d’assistance n’a pas encore été collecté. Alors, faites le nettoyage que vous voulez sur l’object assistant, puis supprimez la PhantomReference et l’aide sera éventuellement collectée.

Il y a aussi finalize (), qui ressemble à un destructeur mais ne se comporte pas comme tel. Ce n’est généralement pas une bonne option.

Je suis désolé si cela ne correspond pas au sujet principal, mais la documentation de java.util.Timer (SE6) dit:

“Une fois que la dernière référence en direct à un object Timer a disparu et que toutes les tâches en attente ont été exécutées, le thread d’exécution des tâches du minuteur se termine normalement (et devient sujet à la récupération de mémoire). le thread d’exécution des tâches ne s’exécute pas comme un thread démon, il est donc capable d’empêcher une application de se terminer. Si un appelant souhaite terminer rapidement le thread d’exécution d’une tâche, l’appelant doit appeler la méthode d’annulation du minuteur … ”

Je voudrais appeler cancel pour que la classe possédant le Timer perd sa dernière référence (ou immeditalesky avant). Ici, un destructeur fiable pourrait le faire pour moi. Les commentaires ci-dessus indiquent que c’est finalement un mauvais choix, mais existe-t-il une solution élégante? Cette affaire de “… capable de bloquer une demande de résiliation …” n’est pas intéressante.

Si c’est juste de la mémoire, tu t’inquiètes, non. Faites confiance au GC qui fait un travail décent. En fait, j’ai vu quelque chose comme si efficace qu’il serait préférable pour les performances de créer des tas d’objects minuscules que d’utiliser de grands tableaux dans certains cas.

La fonction finalize() est le destructeur.

Cependant, il ne devrait pas être utilisé normalement car il est appelé après le GC et vous ne pouvez pas dire quand cela se produira (si jamais).

De plus, il faut plus d’un GC pour désallouer des objects finalize() .

Vous devriez essayer de nettoyer les endroits logiques de votre code en utilisant les instructions try{...} finally{...} !

Je suis d’accord avec la plupart des réponses.

Vous ne devez pas dépendre totalement de la finalize ou du ShutdownHook

finaliser

  1. JVM ne garantit pas lorsque cette méthode finalize() sera invoquée.

  2. finalize() est appelé une seule fois par le thread GC si l’object revit de la méthode de finalisation que la finalisation ne sera plus appelée.

  3. Dans votre application, vous pouvez avoir des objects actifs sur lesquels la récupération de place n’est jamais appelée.

  4. Toute exception émise par la méthode de finalisation est ignorée par le thread GC

  5. System.runFinalization(true) et Runtime.getRuntime().runFinalization(true) augmentent la probabilité d’invoquer la méthode Runtime.getRuntime().runFinalization(true) finalize() , mais ces deux méthodes sont désormais obsolètes. Ces méthodes sont très dangereuses en raison de l’absence de sécurité des threads et de la création possible d’obstacles.

shutdownhooks

 public void addShutdownHook(Thread hook) 

Enregistre un nouveau crochet d’arrêt de la machine virtuelle.

La machine virtuelle Java s’arrête en réponse à deux types d’événements:

  1. Le programme se ferme normalement lorsque le dernier thread non-démon se termine ou lorsque la méthode exit (équivalence, System.exit) est appelée ou
  2. La machine virtuelle est arrêtée en réponse à une interruption utilisateur, telle que la saisie de ^ C ou d’un événement à l’échelle du système, tel que la fermeture de session d’un utilisateur ou l’arrêt du système.
  3. Un hook d’arrêt est simplement un thread initialisé mais non démarré. Lorsque la machine virtuelle commence sa séquence d’arrêt, elle démarre tous les hooks d’arrêt enregistrés dans un ordre non spécifié et les laisse s’exécuter simultanément. Une fois tous les hooks terminés, tous les finaliseurs non invoqués seront exécutés si la finalisation à la sortie a été activée.
  4. Enfin, la machine virtuelle s’arrêtera. Notez que les threads de démon continueront à s’exécuter pendant la séquence d’arrêt, de même que les threads non-démon si l’arrêt a été lancé en invoquant la méthode de sortie.
  5. Les crochets d’arrêt doivent également terminer leur travail rapidement. Lorsqu’un programme appelle exit, il est prévu que la machine virtuelle s’arrête rapidement et quitte.

    Mais même la documentation Oracle a cité que

Dans de rares circonstances, la machine virtuelle peut abandonner, c’est-à-dire cesser de fonctionner sans fermer proprement

Cela se produit lorsque la machine virtuelle est terminée en externe, par exemple avec le signal SIGKILL sur Unix ou l’appel TerminateProcess sur Microsoft Windows. La machine virtuelle peut également abandonner si une méthode native échoue, par exemple en corrompant des structures de données internes ou en tentant d’accéder à une mémoire inexistante. Si la machine virtuelle s’arrête, aucune garantie ne peut être donnée quant à savoir si des hooks d’arrêt seront exécutés ou non.

Conclusion : utilisez try{} catch{} finally{} bloque de manière appropriée et libérez les ressources critiques dans finally(} block. Pendant la publication des ressources dans finally{} block, catch Exception et Throwable .

Vous pouvez peut-être utiliser un bloc try … finally pour finaliser l’object dans le stream de contrôle auquel vous utilisez l’object. Bien sûr, cela ne se produit pas automatiquement, mais la destruction ne se fait pas non plus en C ++. Vous voyez souvent la fermeture des ressources dans le bloc finally.

L’équivalent le plus proche d’un destructeur dans Java est la méthode finalize () . La grande différence avec un destructeur traditionnel est que vous ne pouvez pas savoir quand il sera appelé, car c’est la responsabilité du ramasse-miettes. Je recommande fortement de lire attentivement ce document avant de l’utiliser, car vos modèles RAIA typiques pour les descripteurs de fichiers, etc., ne fonctionneront pas de manière fiable avec finalize ().

Il y a une annotation @Cleanup dans Lombok qui ressemble principalement aux destructeurs C ++:

 @Cleanup ResourceClass resource = new ResourceClass(); 

Lors de son traitement (au moment de la compilation), Lombok insère le bloc try-finally approprié pour que la resource.close() soit appelée, lorsque l’exécution quitte la scope de la variable. Vous pouvez également spécifier explicitement une autre méthode pour libérer la ressource, par exemple, resource.dispose() :

 @Cleanup("dispose") ResourceClass resource = new ResourceClass(); 

Bien qu’il y ait eu des progrès considérables dans la technologie GC de Java, vous devez toujours tenir compte de vos références. On pense à de nombreux cas de motifs de référence apparemment anodins qui sont en réalité des nids de rats sous le capot.

Dans votre message, cela ne ressemble pas à ce que vous essayez d’implémenter une méthode de réinitialisation dans le but de réutiliser un object (true?). Vos objects contiennent-ils d’autres types de ressources à nettoyer (stream fermés, objects regroupés ou empruntés à retourner)? Si la seule chose qui vous inquiète est la gestion de la mémoire, je réexaminerais la structure de mon object et tenterais de vérifier que mes objects sont des structures autonomes qui seront nettoyées au moment du GC.

Si vous écrivez une applet Java, vous pouvez remplacer la méthode Applet “destroy ()”. C’est…

  * Called by the browser or applet viewer to inform * this applet that it is being reclaimed and that it should destroy * any resources that it has allocated. The stop() method * will always be called before destroy(). 

De toute évidence pas ce que vous voulez, mais pourrait être ce que les autres recherchent.

Il suffit de penser à la question originale … que je pense pouvoir tirer de toutes les autres réponses apsockets, ainsi que de l’essentiel 7 de Java efficace de Bloch, «Éviter les finaliseurs», cherche une solution à une question légitime. est inapproprié pour le langage Java …:

… une solution assez évidente ne serait-elle pas de faire en sorte que tous les objects devant être réinitialisés dans une sorte de “parc”, auxquels tous les autres objects non réinitialisables ont des références, soient d’object accesseur …

Et puis, lorsque vous avez besoin de “réinitialiser”, vous déconnectez le parc existant et en créez un nouveau: tout le réseau d’objects du parc est à la dérive, ne retourne jamais et un jour sera collecté par le CPG.

Si l’un de ces objects est Closeable (ou non, mais a une méthode close ), vous pouvez le placer dans un Bag dans le parc au fur et à mesure de sa création (et éventuellement de son ouverture) et le dernier acte de l’accesseur avant de couper le parc être passer par tous les Closeables fermant …?

Le code ressemblerait probablement à ceci:

 accessor.getPlaypen().closeCloseables(); accessor.setPlaypen( new Playpen() ); 

closeCloseables serait probablement une méthode de blocage, impliquant probablement un verrou (par exemple CountdownLatch ), pour gérer (et attendre comme il convient) les Runnables / Callables dans les threads spécifiques au Playpen à terminer, en particulier dans le thread JavaFX. .

Il n’y a pas de classe de destructeur exactement en Java, classe détruite automatiquement dans Java par le garbage collector. mais vous pouvez le faire en utilisant moins d’un mais ce n’est pas exactement la même chose:

finaliser()

Il y avait une question qui a engendré une discussion approfondie sur la finalisation , de sorte que vous devriez obtenir plus de profondeur si nécessaire …

J’avais l’habitude de traiter principalement en C ++ et c’est ce qui m’a amené à rechercher aussi un destructeur. J’utilise beaucoup JAVA maintenant. Ce que j’ai fait, et ce n’est peut-être pas le meilleur pour tout le monde, mais j’ai implémenté mon propre destructeur en réinitialisant toutes les valeurs à 0 ou par défaut via une fonction.

Exemple:

 public myDestructor() { variableA = 0; //INT variableB = 0.0; //DOUBLE & FLOAT variableC = "NO NAME ENTERED"; //TEXT & STRING variableD = false; //BOOL } 

Idéalement, cela ne fonctionnera pas dans toutes les situations, mais là où il y a des variables globales, cela fonctionnera tant que vous n’en avez pas une tonne.

Je sais que je ne suis pas le meilleur programmeur Java, mais cela semble fonctionner pour moi.