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 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?
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):
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é.
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é.
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.