Vous cherchez un aperçu des décisions concernant la conception de la langue de collecte des ordures. Peut-être qu'un expert linguistique pourrait m'éclairer? Je viens d'un milieu C ++, donc ce domaine est déroutant pour moi.
Il semble que presque tous les langages modernes récupérés avec la prise en charge des objets OOPy comme Ruby, Javascript / ES6 / ES7, Actionscript, Lua, etc. omettent complètement le paradigme destructeur / finaliser. Python semble être le seul avec sa class __del__()
méthode. Pourquoi est-ce? Existe-t-il des limitations fonctionnelles / théoriques dans les langages avec ramasse-miettes automatique qui empêchent les implémentations efficaces d'une méthode destructrice / finaliser sur les objets?
Je trouve extrêmement insuffisant que ces langages considèrent la mémoire comme la seule ressource à gérer. Qu'en est-il des sockets, des descripteurs de fichiers, des états d'application? Sans la possibilité d'implémenter une logique personnalisée pour nettoyer les ressources non mémoire et les états lors de la finalisation des objets, je suis obligé de jeter mon application avec des myObject.destroy()
appels de style personnalisé , de placer la logique de nettoyage en dehors de ma "classe", de rompre les tentatives d'encapsulation et de reléguer mon application aux fuites de ressources dues à une erreur humaine plutôt que d'être gérées automatiquement par le GC.
Quelles sont les décisions de conception de langage qui font que ces langages n'ont aucun moyen d'exécuter une logique personnalisée lors de l'élimination des objets? Je dois imaginer qu'il y a une bonne raison. J'aimerais mieux comprendre les décisions techniques et théoriques qui ont eu pour conséquence que ces langages ne prennent pas en charge la destruction / finalisation d'objets.
Mise à jour:
Peut-être une meilleure façon de formuler ma question:
Pourquoi un langage aurait-il le concept intégré d'instances d'objets avec des structures de classe ou de classe avec une instanciation personnalisée (constructeurs), tout en omettant complètement la fonctionnalité de destruction / finalisation? Les langages qui offrent la collecte automatique des ordures semblent être des candidats privilégiés pour prendre en charge la destruction / finalisation d'objets, car ils savent avec une certitude à 100% qu'un objet n'est plus utilisé. Pourtant, la plupart de ces langues ne le prennent pas en charge.
Je ne pense pas que ce soit un cas où le destructeur ne sera jamais appelé, car ce serait une fuite de mémoire centrale, que les gcs sont conçus pour éviter. Je pouvais voir un argument possible étant que le destructeur / finaliseur pourrait ne pas être appelé avant un moment indéterminé à l'avenir, mais cela n'a pas empêché Java ou Python de prendre en charge la fonctionnalité.
Quelles sont les principales raisons de conception du langage pour ne prendre en charge aucune forme de finalisation d'objet?
finalize
/destroy
est un mensonge? Il n'y a aucune garantie qu'il sera jamais exécuté. Et, même si, vous ne savez pas quand (en raison du ramassage automatique des ordures), et si le contexte nécessaire est toujours là (il a peut-être déjà été collecté). Il est donc plus sûr de garantir un état cohérent par d'autres moyens, et on peut vouloir forcer le programmeur à le faire.Réponses:
Le modèle dont vous parlez, où les objets savent comment nettoyer leurs ressources, se divise en trois catégories pertinentes. Ne confondons pas les destructeurs avec les finaliseurs - un seul est lié à la récupération de place:
Le modèle du finaliseur : méthode de nettoyage déclarée automatiquement, définie par le programmeur, appelée automatiquement.
Les finaliseurs sont appelés automatiquement avant la désallocation par un garbage collector. Le terme s'applique si l'algorithme de récupération de place utilisé peut déterminer les cycles de vie des objets.
Le modèle destructeur : méthode de nettoyage déclarée automatiquement, définie par le programmeur, appelée automatiquement seulement parfois.
Les destructeurs peuvent être appelés automatiquement pour les objets alloués à la pile (car la durée de vie des objets est déterministe), mais doivent être explicitement appelés sur tous les chemins d'exécution possibles pour les objets alloués par segment (car la durée de vie des objets n'est pas déterministe).
Le modèle de broyeur : méthode de nettoyage déclarée, définie et appelée par le programmeur.
Les programmeurs créent une méthode d'élimination et l'appellent eux-mêmes - c'est là que votre
myObject.destroy()
méthode personnalisée tombe. Si l'élimination est absolument nécessaire, les éliminateurs doivent être appelés sur tous les chemins d'exécution possibles.Les finaliseurs sont les droïdes que vous recherchez.
Le modèle de finaliseur (le modèle sur lequel porte votre question) est le mécanisme permettant d'associer des objets à des ressources système (sockets, descripteurs de fichiers, etc.) pour une récupération mutuelle par un garbage collector. Mais, les finaliseurs sont fondamentalement à la merci de l'algorithme de collecte des ordures utilisé.
Considérez votre hypothèse:
Techniquement faux (merci @babou). La collecte des ordures concerne fondamentalement la mémoire, pas les objets. Si ou quand un algorithme de collecte se rend compte que la mémoire d'un objet n'est plus utilisée, cela dépend de l'algorithme et (éventuellement) de la façon dont vos objets se réfèrent les uns aux autres. Parlons de deux types de ramasse-miettes d'exécution. Il existe de nombreuses façons de les modifier et de les étendre aux techniques de base:
Tracing GC. Ces traces de mémoire, pas des objets. À moins qu'ils ne soient augmentés, ils ne conservent pas de références en arrière aux objets de la mémoire. Sauf s'ils sont augmentés, ces GC ne sauront pas quand un objet peut être finalisé, même s'ils savent quand sa mémoire est inaccessible. Par conséquent, les appels du finaliseur ne sont pas garantis.
Comptage de référence GC . Ceux-ci utilisent des objets pour suivre la mémoire. Ils modélisent l'accessibilité des objets avec un graphe orienté de références. S'il y a un cycle dans votre graphique de référence d'objet, alors tous les objets du cycle n'auront jamais leur finaliseur appelé (jusqu'à la fin du programme, évidemment). Encore une fois, les appels du finaliseur ne sont pas garantis.
TLDR
La collecte des ordures est difficile et diversifiée. Un appel du finaliseur ne peut être garanti avant la fin du programme.
la source
finalize()
fait que l'objet nettoyé est à nouveau référencé?). Cependant, le fait de ne pas pouvoir garantir que le finaliseur soit appelé avant la fin du programme n'a pas empêché Java de le prendre en charge. Ne pas dire que votre réponse est incorrecte, peut-être juste incomplète. Encore un très bon post. Merci.En un mot
La finalisation n'est pas une affaire simple à gérer par les ramasseurs d'ordures. Il est facile à utiliser avec le GC de comptage de référence, mais cette famille de GC est souvent incomplète, nécessitant des fuites de mémoire pour être compensées par le déclenchement explicite de la destruction et la finalisation de certains objets et structures. Le traçage des collecteurs de déchets est beaucoup plus efficace, mais il est beaucoup plus difficile d'identifier l'objet à finaliser et à détruire, par opposition à simplement identifier la mémoire inutilisée, nécessitant ainsi une gestion plus complexe, avec un coût en temps et en espace, et en complexité de la mise en oeuvre.
introduction
Je suppose que ce que vous demandez, c'est pourquoi les langues de récupération de place ne gèrent pas automatiquement la destruction / finalisation dans le processus de récupération de place, comme indiqué par la remarque:
Je ne suis pas d'accord avec la réponse acceptée donnée par kdbanman . Bien que les faits déclarés soient pour la plupart corrects, bien que fortement biaisés vers le comptage des références, je ne pense pas qu'ils expliquent correctement la situation dénoncée dans la question.
Je ne pense pas que la terminologie développée dans cette réponse soit un gros problème, et elle est plus susceptible de semer la confusion. En effet, comme présenté, la terminologie est principalement déterminée par la façon dont les procédures sont activées plutôt que par ce qu'elles font. Le fait est que dans tous les cas, il est nécessaire de finaliser un objet qui n'est plus nécessaire avec un processus de nettoyage et de libérer toutes les ressources qu'il a utilisées, la mémoire n'étant que l'une d'entre elles. Idéalement, tout cela devrait être fait automatiquement lorsque l'objet n'est plus utilisé, au moyen d'un ramasse-miettes. Dans la pratique, le GC peut être manquant ou présenter des lacunes, ce qui est compensé par un déclenchement explicite par le programme de finalisation et de remise en état.
Le déclenchement explicite par le programme est un problème car il peut permettre d'analyser difficilement les erreurs de programmation, lorsqu'un objet encore en cours d'utilisation se termine explicitement.
Par conséquent, il est préférable de s'appuyer sur la collecte automatique des ordures pour récupérer les ressources. Mais il y a deux problèmes:
certaines techniques de récupération de place permettront des fuites de mémoire qui empêcheront la récupération complète des ressources. Ceci est bien connu pour le calcul de référence GC, mais peut apparaître pour d'autres techniques de GC lors de l'utilisation de certaines organisations de données sans précaution (point non discuté ici).
Bien que la technique GC puisse être efficace pour identifier les ressources de mémoire qui ne sont plus utilisées, la finalisation des objets qui y sont contenus peut ne pas être simple, et cela complique le problème de la récupération d'autres ressources utilisées par ces objets, ce qui est souvent le but de la finalisation.
Enfin, un point important souvent oublié est que les cycles de GC peuvent être déclenchés par n'importe quoi, pas seulement par une pénurie de mémoire, si les crochets appropriés sont fournis et si le coût d'un cycle de GC est considéré comme valable. Par conséquent, il est tout à fait correct d'initier un GC quand n'importe quel type de ressource est manquant, dans l'espoir d'en libérer certaines.
Compter les ordures ménagères
Le comptage des références est une technique de collecte des ordures faible , qui ne gère pas les cycles correctement. Il serait en effet faible sur la destruction de structures obsolètes et la récupération d'autres ressources simplement parce qu'il est faible sur la récupération de la mémoire. Mais les finaliseurs peuvent être utilisés plus facilement avec un récupérateur de déchets de comptage de référence (GC), car un GC de comptage de référence récupère une structure lorsque son nombre de références descend à 0, moment auquel son adresse est connue avec son type, soit statiquement ou dynamiquement. Par conséquent, il est possible de récupérer la mémoire précisément après avoir appliqué le finaliseur approprié et appelé récursivement le processus sur tous les objets pointés (éventuellement via la procédure de finalisation).
En un mot, la finalisation est facile à implémenter avec Ref Counting GC, mais souffre de l '"incomplétude" de ce GC, en raison des structures circulaires, exactement dans la même mesure que la récupération de mémoire en souffre. En d'autres termes, avec le nombre de références, la mémoire est précisément aussi mal gérée que d'autres ressources telles que les sockets, les descripteurs de fichiers, etc.
En effet, l' incapacité de Ref Count GC à récupérer des structures en boucle (en général) peut être considérée comme une fuite de mémoire . Vous ne pouvez pas vous attendre à ce que tous les CPG évitent les fuites de mémoire. Cela dépend de l'algorithme GC et des informations de structure de type disponibles dynamiquement (par exemple dans GC conservateur ).
Traçage des collecteurs d'ordures
La famille de GC la plus puissante, sans ces fuites, est la famille de traçage qui explore les parties actives de la mémoire, à partir de pointeurs racine bien identifiés. Toutes les parties de la mémoire qui ne sont pas visitées dans ce processus de traçage (qui peuvent en fait être décomposées de diverses manières, mais je dois simplifier) sont des parties inutilisées de la mémoire qui peuvent ainsi être récupérées 1 . Ces collecteurs récupéreront toutes les parties de la mémoire auxquelles le programme ne pourra plus accéder, quoi qu'il fasse. Il récupère des structures circulaires, et les GC les plus avancés sont basés sur une certaine variation de ce paradigme, parfois très sophistiqué. Il peut être combiné avec le comptage des références dans certains cas et compenser ses faiblesses.
Un problème est que votre déclaration (à la fin de la question):
est techniquement incorrect pour le traçage des collecteurs.
Ce que l'on sait avec certitude à 100%, c'est quelles parties de la mémoire ne sont plus utilisées . (Plus précisément, il faut dire qu'elles ne sont plus accessibles , car certaines parties, qui ne peuvent plus être utilisées selon la logique du programme, sont toujours considérées comme utilisées s'il y a encore un pointeur inutile vers elles dans le programme Mais un traitement supplémentaire et des structures appropriées sont nécessaires pour savoir quels objets inutilisés peuvent avoir été stockés dans ces parties de la mémoire qui ne sont plus utilisées . Cela ne peut pas être déterminé à partir de ce que l'on sait du programme, car le programme n'est plus connecté à ces parties de la mémoire.
Ainsi après un passage de garbage collection, vous vous retrouvez avec des fragments de mémoire qui contiennent des objets qui ne sont plus utilisés, mais il n'y a a priori aucun moyen de savoir ce que sont ces objets pour appliquer la finalisation correcte. De plus, si le collecteur de traçage est de type marquage et balayage, il se peut que certains des fragments contiennent des objets qui ont déjà été finalisés dans une passe GC précédente, mais qui n'ont pas été utilisés depuis pour des raisons de fragmentation. Cependant, cela peut être résolu en utilisant un typage explicite étendu.
Alors qu'un simple collectionneur récupérerait simplement ces fragments de mémoire, sans plus tarder, la finalisation nécessite une passe spécifique pour explorer cette mémoire inutilisée, identifier les objets qu'elle contenait et appliquer les procédures de finalisation. Mais une telle exploration nécessite la détermination du type d'objets qui y étaient stockés, et la détermination du type est également nécessaire pour appliquer la finalisation appropriée, le cas échéant.
Cela implique donc des coûts supplémentaires en temps GC (le passage supplémentaire) et éventuellement des coûts de mémoire supplémentaires pour rendre les informations de type appropriées disponibles pendant ce passage par diverses techniques. Ces coûts peuvent être importants dans la mesure où l'on ne souhaite souvent finaliser que quelques objets, tandis que le temps et l'espace requis peuvent concerner tous les objets.
Un autre point est que la surcharge de temps et d'espace peut concerner l'exécution du code du programme, et pas seulement l'exécution du GC.
Je ne peux pas donner de réponse plus précise, en pointant des questions spécifiques, car je ne connais pas les spécificités de nombreuses langues que vous citez. Dans le cas de C, la dactylographie est une question très difficile qui conduit au développement de collecteurs conservateurs. Je suppose que cela affecte également C ++, mais je ne suis pas un expert en C ++. Cela semble être confirmé par Hans Boehm qui a fait une grande partie de la recherche sur GC conservateur. GC conservateur ne peut pas récupérer systématiquement toute la mémoire inutilisée précisément car il peut manquer d'informations de type précises sur les données. Pour la même raison, il ne serait pas en mesure d'appliquer systématiquement les procédures de finalisation.
Il est donc possible de faire ce que vous demandez, comme vous le savez dans certaines langues. Mais cela ne vient pas gratuitement. Selon la langue et sa mise en œuvre, cela peut entraîner un coût même si vous n'utilisez pas la fonctionnalité. Diverses techniques et compromis peuvent être envisagés pour résoudre ces problèmes, mais cela dépasse la portée d'une réponse de taille raisonnable.
1 - il s'agit d'une présentation abstraite de la collection de traçage (englobant à la fois le GC de copie et de marquage et de balayage), les choses varient selon le type de collecteur de traçage, et l'exploration de la partie inutilisée de la mémoire est différente, selon qu'il s'agit d'une copie ou d'une marque et balayage est utilisé.
la source
getting memory recycled
ce que j'appellereclamation
et qui effectue un nettoyage avant cela, comme récupérer d'autres ressources ou mettre à jour certaines tables d'objets, que j'appellefinalization
. Ces questions m'ont semblé pertinentes, mais j'ai peut-être manqué un point dans votre terminologie, qui était nouveau pour moi.Le modèle de destructeur d'objets est fondamental pour la gestion des erreurs dans la programmation des systèmes, mais n'a rien à voir avec la récupération de place. Il s'agit plutôt de faire correspondre la durée de vie d'un objet à une étendue, et peut être implémenté / utilisé dans n'importe quel langage ayant des fonctions de première classe.
Exemple (pseudocode). Supposons que vous ayez un type de "fichier brut", comme le type de descripteur de fichier Posix. Il y a quatre opérations fondamentales,
open()
,close()
,read()
,write()
. Vous souhaitez implémenter un type de fichier "sûr" qui nettoie toujours après lui-même. (C'est-à-dire, qui a un constructeur et un destructeur automatiques.)Je suppose que notre langue a une gestion des exceptions avec
throw
,try
etfinally
(dans les langues sans gestion des exceptions, vous pouvez configurer une discipline dans laquelle l'utilisateur de votre type renvoie une valeur spéciale pour indiquer une erreur.)Vous configurez une fonction qui accepte une fonction qui fait le travail. La fonction de travail accepte un argument (un handle vers le fichier "sûr").
Vous fournissez également des implémentations de
read()
etwrite()
poursafe_file
(qui appellent simplement leraw_file
read()
etwrite()
). Maintenant, l'utilisateur utilise lesafe_file
type comme ceci:Un destructeur C ++ est vraiment juste du sucre syntaxique pour un
try-finally
bloc. À peu près tout ce que j'ai fait ici est de convertir ce qu'unesafe_file
classe C ++ avec un constructeur et un destructeur compilerait. Notez que C ++ n'a pasfinally
d'exceptions, en particulier parce que Stroustrup a estimé que l'utilisation d'un destructeur explicite était meilleure syntaxiquement (et il l'a introduit dans le langage avant que le langage ait des fonctions anonymes).(Il s'agit d'une simplification de l'une des façons dont les gens gèrent les erreurs dans des langages de type Lisp depuis de nombreuses années. Je pense que je l'ai rencontré à la fin des années 1980 ou au début des années 1990, mais je ne me souviens pas où.)
la source
safe_file
etwith_file_opened_for_read
(un objet qui se ferme quand il sort du domaine ). C'est la chose importante, qu'il n'a pas la même syntaxe que les constructeurs n'est pas pertinent. Lisp, Scheme, Java, Scala, Go, Haskell, Rust, Javascript, Clojure prennent tous en charge des fonctions de première classe suffisantes, ils n'ont donc pas besoin de destructeurs pour fournir la même fonctionnalité utile.Ce n'est pas une réponse complète à la question, mais je voulais ajouter quelques observations qui n'ont pas été couvertes dans les autres réponses ou commentaires.
La question suppose implicitement que nous parlons d'un langage orienté objet de style Simula, qui est lui-même limitant. Dans la plupart des langues, même celles avec des objets, tout n'est pas un objet. Le mécanisme pour implémenter les destructeurs imposerait un coût que tous les implémenteurs de langues ne sont pas prêts à payer.
C ++ a des garanties implicites sur l'ordre de destruction. Si vous avez une structure de données arborescente, par exemple, les enfants seront détruits avant le parent. Ce n'est pas le cas dans les langues GC'd, donc les ressources hiérarchiques peuvent être publiées dans un ordre imprévisible. Pour les ressources autres que la mémoire, cela peut être important.
la source
Lorsque les deux cadres GC les plus populaires (Java et .NET) ont été conçus, je pense que les auteurs s'attendaient à ce que la finalisation fonctionne suffisamment bien pour éviter le besoin d'autres formes de gestion des ressources. De nombreux aspects de la conception du langage et du framework peuvent être grandement simplifiés s'il n'est pas nécessaire de disposer de toutes les fonctionnalités nécessaires pour permettre une gestion des ressources 100% fiable et déterministe. En C ++, il est nécessaire de distinguer les concepts de:
Pointeur / référence qui identifie un objet qui appartient exclusivement au titulaire de la référence et qui n'est identifié par aucun pointeur / référence que le propriétaire ne connaît pas.
Pointeur / référence identifiant un objet partageable qui n'appartient à personne.
Pointeur / référence qui identifie un objet qui est exclusivement détenu par le titulaire de la référence, mais qui peut être accessible par des « vues » le propriétaire n'a aucun moyen de suivi.
Pointeur / référence qui identifie un objet qui fournit une vue d'un objet qui appartient à quelqu'un d'autre.
Si un langage / framework GC n'a pas à se soucier de la gestion des ressources, tout ce qui précède peut être remplacé par un seul type de référence.
Je trouverais naïve l'idée que la finalisation éliminerait le besoin d'autres formes de gestion des ressources, mais que cette attente soit ou non raisonnable à l'époque, l'histoire a depuis montré qu'il existe de nombreux cas qui nécessitent une gestion des ressources plus précise que celle fournie par la finalisation. . Il se trouve que je pense que les récompenses de la reconnaissance de la propriété au niveau de la langue / du cadre seraient suffisantes pour justifier le coût (la complexité doit exister quelque part, et le déplacer vers le langage / le cadre simplifierait le code utilisateur), mais je reconnais qu'il existe d'importantes les avantages de la conception d'avoir un seul "type" de référence - quelque chose qui ne fonctionne que si le langage / le framework est indépendant des problèmes de nettoyage des ressources.
la source
Le destructeur en C ++ fait en fait deux choses combinées. Il libère de la RAM et libère des identifiants de ressources.
D'autres langues séparent ces préoccupations en confiant au GC la responsabilité de libérer la RAM tandis qu'une autre fonctionnalité linguistique prend en charge la libération des identifiants des ressources.
C'est à cela que servent les GC. Ils ne font qu'une chose et c'est pour s'assurer que vous ne manquiez pas de mémoire. Si la RAM est infinie, tous les GC seraient retirés car il n'y a plus de raison réelle d'exister.
Les langues peuvent fournir différentes manières de libérer les identifiants de ressource en:
manuel
.CloseOrDispose()
dispersé dans le codemanuel
.CloseOrDispose()
dispersé dans un "finally
bloc" manuelmanuelle « blocs id ressources » (c. -à
using
,with
,try
-Avec-ressources , etc.) qui automatise.CloseOrDispose()
après le bloc effectuées"blocs d'identification des ressources" garantis qui automatisent une
.CloseOrDispose()
fois le bloc terminéDe nombreuses langues utilisent des mécanismes manuels (par opposition à garantis) qui créent une opportunité de mauvaise gestion des ressources. Prenez ce simple code NodeJS:
..où le programmeur a oublié de fermer le fichier ouvert.
Tant que le programme continue de fonctionner, le fichier ouvert est bloqué dans les limbes. Ceci est facile à vérifier en essayant d'ouvrir le fichier à l'aide de HxD et en vérifiant que cela ne peut pas être fait:
La libération des ID de ressource dans les destructeurs C ++ n'est pas non plus garantie. Vous pourriez penser que RAII fonctionne comme des "blocs d'ID de ressource" garantis, mais contrairement aux "blocs d'ID de ressource", le langage C ++ n'empêche pas l'objet fournissant le bloc RAII de fuir , donc le bloc RAII peut ne jamais être fait .
Parce qu'ils gèrent les identifiants de ressource en utilisant d'autres méthodes, comme mentionné ci-dessus.
Parce qu'ils gèrent les identifiants de ressource en utilisant d'autres méthodes, comme mentionné ci-dessus.
Parce qu'ils gèrent les identifiants de ressource en utilisant d'autres méthodes, comme mentionné ci-dessus.
Java n'a pas de destructeurs.
Les documents Java mentionnent :
..mais mettre le code de gestion des identifiants de ressource à l'intérieur
Object.finalizer
est largement considéré comme un anti-modèle ( cf. ). Ce code devrait plutôt être écrit sur le site de l'appel.Pour les personnes qui utilisent l'anti-modèle, leur justification est qu'elles ont peut-être oublié de libérer les identifiants de ressource sur le site d'appel. Ainsi, ils recommencent dans le finaliseur, au cas où.
Il n'y a pas beaucoup de cas d'utilisation pour les finaliseurs car ils servent à exécuter un morceau de code entre le moment où il n'y a plus de références fortes à l'objet et le moment où sa mémoire est récupérée par le GC.
Un cas d'utilisation possible est lorsque vous souhaitez conserver un journal du temps entre la collecte de l'objet par le GC et le moment où il n'y a plus de références fortes à l'objet, en tant que tel:
la source
trouvé une référence à ce sujet dans le Dr Dobbs wrt c ++ qui a des idées plus générales qui soutiennent que les destructeurs sont problématiques dans un langage où ils sont implémentés. une idée approximative semble être ici que l'un des principaux objectifs des destructeurs est de gérer la désallocation de mémoire, ce qui est difficile à réaliser correctement. la mémoire est allouée par morceaux mais différents objets sont connectés et la responsabilité / les limites de désallocation ne sont pas si claires.
donc la solution à cela d'un garbage collector a évolué il y a des années, mais le garbage collection n'est pas basé sur des objets disparaissant de la portée aux sorties de méthode (c'est une idée conceptuelle qui est difficile à implémenter), mais sur un collecteur fonctionnant périodiquement, quelque peu de façon non déterministe, lorsque l'application subit une «pression sur la mémoire» (c'est-à-dire un manque de mémoire).
en d'autres termes, le simple concept humain d'un "objet nouvellement inutilisé" est en fait à certains égards une abstraction trompeuse en ce sens qu'aucun objet ne peut "instantanément" devenir inutilisé. les objets inutilisés ne peuvent être "découverts" qu'en exécutant un algorithme de récupération de place qui parcourt le graphe de référence d'objet et les algorithmes les plus performants s'exécutent par intermittence.
il est possible qu'un meilleur algorithme de collecte des ordures soit à découvrir qui puisse identifier presque instantanément les objets inutilisés, ce qui pourrait alors conduire à un code d'appel destructeur cohérent, mais aucun n'a été trouvé après de nombreuses années de recherche dans le domaine.
la solution aux domaines de gestion des ressources tels que les fichiers ou les connexions semble être d'avoir des «gestionnaires» d'objets qui tentent de gérer leur utilisation.
la source