Pourquoi les langages tels que C et C ++ n'ont-ils pas de récupération de place, contrairement à Java? [fermé]

57

Eh bien, je sais qu'il existe des choses comme malloc / free for C, et new / using-a-destructor pour la gestion de la mémoire en C ++, mais je me demandais pourquoi il n'y a pas de "nouvelles mises à jour" dans ces langages qui permettent à l'utilisateur de avoir la possibilité de gérer manuellement la mémoire, ou que le système le fasse automatiquement (nettoyage de la mémoire)?

Une question plutôt nouvelle, mais qui ne figure dans CS que depuis environ un an.

Dark Templar
la source
9
Nous avons un module en développement iPhone ce semestre. Après avoir codé des applications pour Android pendant 2 ans, cette question a frappé assez durement la classe. Ce n’est que maintenant que nous voyons le nombre d’heures que Java nous a réellement économisées en nous évitant de dépister des erreurs de gestion de la mémoire et en n’écrivant pas le code de la plaque de la chaudière.
siamii
7
@NullUserException, car il ne spécifie pas un moyen de récupérer de la mémoire qui implique à peu près un GC.
Winston Ewert
1
@ bizso09: Avez-vous déjà consulté ARC? Pas besoin de GC lent / gras / non déterministe quand vous avez le support système pour le comptage de références: developer.apple.com/technologies/ios5
JBRWilkinson
3
Les réponses à cette très bonne question sont pleines de conneries religieuses.
Abatishchev
1
En C et C ++, il est possible de prendre un pointeur, de le convertir en int et de lui ajouter un numéro. Ultérieurement, soustrayez le nombre du entier et convertissez le résultat en pointeur. Vous obtiendrez le même pointeur qu'avant. Bonne chance dans la mise en œuvre d'un CPG qui ne collecte PAS cette mémoire alors que son adresse est stockée uniquement dans la variable qui a également une autre valeur. Je sais que l'exemple est stupide, mais une liste chaînée XOR utilise quelque chose de similaire. Je voudrais poster ceci comme une réponse, mais la question est fermée.
Marian Spanik

Réponses:

71

Le ramassage des ordures nécessite des structures de données pour suivre les allocations et / ou le comptage des références. Celles-ci créent une surcharge en mémoire, en performances et en complexité du langage. C ++ est conçu pour être "proche du métal", autrement dit, il prend le côté plus performant des fonctionnalités de compromis par rapport à la commodité. D'autres langues font ce compromis différemment. C’est l’une des considérations à prendre en compte lors du choix d’une langue, l’emphase que vous préférez.

Cela dit, de nombreux schémas de comptage de références en C ++ sont assez légers et performants, mais ils se trouvent dans des bibliothèques, à la fois commerciales et à code source ouvert, plutôt qu’une partie du langage lui-même. Compter les références pour gérer la durée de vie des objets n’est pas la même chose que le garbage collection, mais il résout bon nombre des mêmes problèmes et s’adapte mieux à l’approche de base de C ++.

poids
la source
26
Un problème secondaire est que le GC n'est pas déterministe. L'objet peut rester ou non en mémoire longtemps après que le programme l'a "abandonné". Les cycles de vie Refcount sont déterministes, lorsque la dernière référence est supprimée, la mémoire est supprimée. Cela a des implications non seulement pour l'efficacité de la mémoire, mais également pour le débogage. Une erreur de programmation courante est l’objet "zombie", une mémoire de référence théoriquement abandonnée. Le GC est beaucoup plus susceptible de masquer cet effet et de générer des bugs intermittents et extrêmement difficiles à détecter.
Kylben
22
- les gc modernes ne font pas de suivi des allocations ou ne comptent pas les références. Ils construisent un graphique à partir de tout ce qui se trouve actuellement sur la pile et ne font que condenser et effacer tout le reste (simplifié). Le GC entraîne généralement une réduction de la complexité du langage. Même le bénéfice de performance est discutable.
Joel Coehoorn
13
Euh, @kylben, l’intérêt d’avoir un GC automatique dans le langage est qu’il devient impossible de référencer des objets zombies, car le GC ne libère que les objets impossibles à référencer! Vous obtenez le type de bogues difficiles à dépister dont vous parlez lorsque vous commettez des erreurs avec la gestion manuelle de la mémoire.
Ben
14
-1, GC ne compte pas les références. De plus, en fonction de votre utilisation de la mémoire et de votre schéma d'allocation, un CPG peut être plus rapide (avec une surcharge d'utilisation de la mémoire). Donc, l'argument de la performance est aussi une erreur. Seul le proche du métal est un point valable en fait.
deadalnix
14
Comptage de références Java ou C #: les schémas GC basés sur le comptage de références sont assez primitifs en comparaison et fonctionnent beaucoup moins bien que les éboueurs modernes (principalement parce qu’ils ont besoin de faire des écritures en mémoire pour modifier les décomptes chaque fois que vous copiez une référence!)
mikera
44

À proprement parler, il n’existe aucune gestion de la mémoire en langage C. malloc () et free () ne sont pas des mots-clés du langage, mais simplement des fonctions appelées à partir d'une bibliothèque. Cette distinction est peut-être pédante à présent, car malloc () et free () font partie de la bibliothèque standard C et seront fournies par toute implémentation conforme de C au standard, mais ce n’était pas toujours le cas par le passé.

Pourquoi voudriez-vous un langage sans standard pour la gestion de la mémoire? Cela remonte aux origines de C en tant qu '"assemblage portable". Il existe de nombreux cas de matériel et d'algorithmes pouvant tirer parti, voire nécessitant, des techniques de gestion de la mémoire spécialisées. À ma connaissance, il n’existe aucun moyen de désactiver complètement la gestion de la mémoire native de Java et de la remplacer par la vôtre. Ce n'est tout simplement pas acceptable dans certaines situations de ressources élevées / hautes performances. C offre une flexibilité presque complète pour choisir exactement l'infrastructure que votre programme utilisera. Le prix à payer est que le langage C fournit très peu d'aide pour écrire un code correct, sans bug.

Charles E. Grant
la source
2
+1 pour la bonne réponse générale, mais aussi surtout pour "Le prix à payer est que le langage C fournit très peu d'aide pour l'écriture d'un code correct et sans bug"
Shivan Dragon
2
C a la gestion de la mémoire - mais ça marche, donc les gens le remarquent à peine. Il y a la mémoire statique, les registres et la pile. Jusqu'à ce que vous commenciez à allouer du tas, tout va bien. C'est l'allocation de tas qui gâche les choses. Pour ce qui est de Java, chacun peut écrire son propre runtime Java - il y a beaucoup de choix, y compris ce qu'on pourrait appeler "le système Java". .NET peut faire à peu près tout ce que le C peut faire - il ne fait que prendre du retard par rapport aux capacités natives de C ++ (par exemple, les classes ne sont gérées que dans .NET). Bien entendu, vous disposez également de C ++. NET, qui regroupe tout ce que fait C ++ et tout ce que fait NET.
Luaan
1
@Luaan Je dirais que c'est une définition très généreuse de "gestion de la mémoire" "Jusqu'à ce que vous commenciez à allouer du tas, tout va bien. C'est l'allocation du tas qui gâche le problème", c'est comme dire qu'une voiture est un avion parfaitement bon, il n'est tout simplement pas capable de voler.
Charles E. Grant
1
@ CharlesE.Grant Eh bien, un langage purement fonctionnel peut tout faire avec ce type de gestion de la mémoire. Le fait que l’allocation de tas soit un bon compromis dans certains cas d’utilisation ne signifie pas que c’est la référence pour toutes les langues / exécutions. Ce n’est pas comme si la gestion de la mémoire cessait d’être une "gestion de la mémoire" simplement parce que c’était simple, directe, cachée dans les coulisses. La conception de l'allocation de mémoire statique reste une gestion de la mémoire, tout comme une utilisation judicieuse de la pile et de tout ce dont vous disposez.
Luaan
"toute implémentation conforme aux normes" n'est pas vrai, mais uniquement pour une implémentation d'environnement hôte conforme aux normes. Certaines plates-formes / bibliothèques standard, pour la plupart des microcontrôleurs intégrés 8 ou 16 bits, ne fournissent pas malloc()ou free(). (par exemple, des compilateurs MLAP pour PIC)
12431234123412341234123
32

La vraie réponse est que le seul moyen de créer un mécanisme de collecte des ordures sûr et efficace consiste à prendre en charge les références opaques au niveau de la langue . (Ou, inversement, un manque de prise en charge au niveau de la langue pour la manipulation directe de la mémoire.)

Java et C # peuvent le faire car ils ont des types de référence spéciaux qui ne peuvent pas être manipulés. Cela donne au moteur d'exécution la liberté de déplacer des objets alloués en mémoire , ce qui est essentiel pour une implémentation GC haute performance.

Pour mémoire, aucune implémentation moderne du GC n’utilise le comptage de références , ce qui en fait un véritable casse-tête. Les GC modernes utilisent la collection générationnelle, où les nouvelles allocations sont traitées essentiellement de la même manière que les allocations de pile dans un langage tel que C ++, puis tous les objets nouvellement alloués encore en vie sont déplacés vers un espace "survivant" séparé et une génération entière. des objets est désalloué à la fois.

Cette approche a des avantages et des inconvénients: l'avantage réside dans le fait que les allocations de segment de mémoire dans un langage prenant en charge le GC sont aussi rapides que les allocations de pile dans un langage ne prenant pas en charge le GC, et l'inconvénient est que les objets devant être nettoyés avant d'être détruits besoin d' un mécanisme distinct (par exemple C de # usingmot - clé) ou bien leur code de nettoyage fonctionne non déterministe.

Notez que l'une des clés d'un CPG hautes performances est la prise en charge linguistique d'une classe de références spécifique. C n'a pas cette langue et ne le fera jamais; Comme C ++ a une surcharge d'opérateur, il pourrait émuler un type de pointeur GC, bien que cela devrait être fait avec précaution. En fait, lorsque Microsoft a inventé son dialecte C ++ qui s'exécutait sous le CLR (le runtime .NET), il a fallu inventer une nouvelle syntaxe pour "références de style C #" (par exemple Foo^) afin de les distinguer des "références de style C ++". (par exemple Foo&).

Ce que C ++ a, et que les programmeurs C ++ utilisent régulièrement , ce sont des pointeurs intelligents , qui ne sont en réalité qu'un mécanisme de comptage de références. Je ne considérerais pas le comptage de références comme un "vrai" GC, mais il offre bon nombre des mêmes avantages, au détriment des performances plus lentes que la gestion manuelle de la mémoire ou le véritable GC, mais avec l’avantage de la destruction déterministe.

À la fin de la journée, la réponse se résume vraiment à une fonctionnalité de conception de langage. C a fait un choix, C ++ a fait un choix qui lui a permis d’être rétro-compatible avec le C tout en offrant des alternatives assez bonnes pour la plupart des applications, et Java et C # ont fait un choix différent, incompatible avec le C, mais également suffisant pour la plupart des fins. Malheureusement, il n’ya pas de solution miracle, mais connaître les différents choix qui s’offrent à vous vous aidera à choisir celui qui convient le mieux au programme que vous essayez de créer.

Daniel Pryden
la source
4
Telle est la réponse réelle à la question
coredump
1
Pour la partie c ++, vous devriez maintenant regarder std :: unique_ptr et std :: move :)
Niclas Larsson
@ NiclasLarsson: Je ne suis pas sûr de comprendre votre argument. Voulez-vous dire qu'il std::unique_ptrs'agit d'un "support de niveau de langue pour les références opaques"? (Ce n'était pas le type d'assistance que je voulais dire et je ne pense pas non plus que cela soit suffisant si la prise en charge de la manipulation directe de mémoire n'était pas supprimée du C ++.) Je mentionne les pointeurs intelligents dans ma réponse et je considérerais std:unique_ptrun pointeur intelligent , puisqu'il effectue effectivement le comptage de références, il ne prend en charge que les cas particuliers où le nombre de références est égal à zéro ou à un (et std::moveconstitue le mécanisme de mise à jour du décompte de références).
Daniel Pryden
std::unique_ptrn'a pas de compte de références et std::moven'a rien à voir avec les références (donc "non" touché de performance). Je vois cependant votre argument, de std::shared_ptrmême que le décompte de références implicite mis à jour par std::move:)
Niclas Larsson
2
@ Mike76: Du côté de l'allocation, un allocateur de CPG fonctionnera à peu près aussi rapidement que l'allocation de pile, et le GC peut libérer des milliers d'objets en même temps. Quoi que vous fassiez avec une implémentation avec décompte, l’allocation et la désallocation ne seront jamais plus rapides que mallocet free. Alors oui, un GC peut être considérablement plus rapide. (Notez que j'ai dit "peut être" - bien sûr, de nombreux facteurs affectent les performances exactes de chaque programme.)
Daniel Pryden
27

Parce que, lorsque vous utilisez la puissance de C ++, il n’est pas nécessaire.

Herb Sutter: " Je n'ai pas écrit delete depuis des années. "

voir Écrire du code C ++ moderne: comment le C ++ a évolué au fil des ans 21:10

Cela peut surprendre de nombreux programmeurs C ++ expérimentés.

Lior Kogan
la source
Intéressant. Mon matériel de lecture pour aujourd'hui.
surfasb
Bah, une vidéo. Mais néanmoins, intéressant déjà.
surfasb
2
vidéo intéressante. 21 minutes et 55 minutes étaient les meilleurs morceaux. Dommage que les appels WinRT aient toujours l'air d'être bumpf C ++ / CLI.
gbjbaanb
2
@ dan04: c'est vrai. Mais ensuite, si vous écrivez en C, vous obtenez ce que vous demandez.
DeadMG
6
La gestion des pointeurs intelligents n’est pas plus exigeante que de s’assurer que vous n’avez pas de références inutiles dans un environnement de récupération. Parce que GC ne peut pas lire dans vos pensées, ce n'est pas magique non plus.
Tamás Szelei
15

"Tout", un ramasse-miettes est un processus qui s'exécute périodiquement pour vérifier s'il y a des objets non référencés en mémoire et s'il y a des suppressions. (Oui, je sais que c'est une simplification excessive). Ce n'est pas une propriété du langage, mais le cadre.

Il y a des éboueurs écrits pour C et C ++ - celui-ci par exemple.

Une des raisons pour lesquelles on n'a pas "ajouté" à la langue pourrait être due au volume même de code existant qui ne l'utilisera jamais car ils utilisent leur propre code pour gérer la mémoire. Une autre raison peut être que les types d'applications écrites en C et C ++ n'ont pas besoin de la surcharge associée à un processus de récupération de place.

ChrisF
la source
1
Mais les futurs programmes écrits commenceraient à utiliser le ramasse-miettes, non?
Dark Templar
5
Alors que la récupération de place est théoriquement indépendante de tout langage de programmation, il est assez difficile d'écrire un GC utile pour C / C ++, et même impossible d'en créer un indéréglable (du moins aussi infaillible que Java) - la raison pour laquelle Java peut le tirer off est parce qu’il s’exécute dans un environnement virtualisé contrôlé. Inversement, le langage Java est conçu pour le GC, et vous aurez du mal à écrire un compilateur Java qui ne le fasse pas.
tdammers
4
@tdammers: Je conviens que la récupération de place doit être supportée par la langue pour être possible. Cependant, l’essentiel n’est pas la virtualisation et l’environnement contrôlé, mais le typage strict. C et C ++ sont faiblement typés, ils permettent donc de stocker un pointeur dans une variable entière, de reconstruire des pointeurs à partir de décalages et d’empêcher le collecteur de déterminer de manière fiable ce qui est accessible (C ++ 11 interdit à ce dernier d’autoriser au moins collectionneurs conservateurs). En Java, vous savez toujours ce qu’est une référence, vous pouvez donc la collecter avec précision, même si elle est compilée en natif.
Jan Hudec
2
@ ThorbjørnRavnAndersen: Je peux écrire un programme C valide qui stocke les pointeurs de manière à ce qu'aucun éboueur ne puisse les trouver. Si vous accrochez ensuite un ramasse-miettes à mallocet free, vous interrompez mon programme correct.
Ben Voigt
2
@ ThorbjørnRavnAndersen: Non, je n'appellerais pas freeavant d'avoir fini. Mais votre ramasse-miettes proposé qui ne libère pas la mémoire tant que je n'ai freepas appelé explicitement n'est pas du tout un ramasse-miettes.
Ben Voigt
12

C a été conçu à une époque où la collecte des ordures ménagères était à peine une option. Il était également destiné aux utilisations pour lesquelles la récupération de place ne fonctionnerait généralement pas - environnements sans système d'exploitation, environnements en temps réel avec une mémoire minimale et une prise en charge minimale de l'exécution. Rappelez-vous que C était le langage d'implémentation du premier Unix, qui s'exécutait sur un pdp-11 avec 64 * K * octets de mémoire. Le C ++ était à l'origine une extension du C - le choix avait déjà été fait et il est très difficile de greffer la récupération de place sur un langage existant. C'est le genre de chose qui doit être construit à partir du rez-de-chaussée.

Ddyer
la source
9

Je n'ai pas les citations exactes mais Bjarne et Herb Sutter disent quelque chose dans le sens suivant:

C ++ n'a pas besoin d'un ramasse-miettes, car il n'a pas d'ordures.

Dans le C ++ moderne, vous utilisez des pointeurs intelligents et vous ne perdez donc pas de temps.

Ronag
la source
1
Que sont les pointeurs intelligents?
Dark Templar
11
si c'était aussi simple, personne n'aurait mis en œuvre de GC.
deadalnix
7
@deadalnix: C'est vrai, car personne ne met en œuvre quoi que ce soit de trop compliqué, de lent, de trop gonflé ou d'inutile. Tous les logiciels sont efficaces à 100% tout le temps, non?
Zach
5
@deadalnix - L'approche C ++ de la gestion de la mémoire est plus récente que celle des garbage collectors. RAII a été inventé par Bjarne Stroustrup pour C ++. La suppression des destructeurs est une idée ancienne, mais les règles visant à garantir la sécurité des exceptions sont essentielles. Je ne sais pas quand exactement quand l'idée elle-même a été décrite, mais le premier standard C ++ a été finalisé en 1998 et Stroustrups "Conception et évolution du C ++" n'a été publié qu'en 1994, et les exceptions sont un ajout relativement récent au C ++ - après la publication du "Manuel de référence C ++ annoté" en 1990, je crois. GC a été inventé en 1959 pour Lisp.
Steve314
1
@deadalnix - savez-vous qu'au moins une machine virtuelle Java a utilisé un CPG de comptage de références pouvant (presque) être implémenté à l'aide de RAII de style C ++ à l'aide d'une classe de pointeur intelligent - précisément parce qu'elle était plus efficace pour le code multithread que les ordinateurs virtuels existants? Voir www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf. Une des raisons pour lesquelles vous ne voyez pas cela en pratique dans C ++ est la collection GC habituelle: elle peut collecter des cycles, mais ne peut pas choisir un ordre de destruction sûr en présence de cycles et ne peut donc pas garantir un nettoyage fiable des destructeurs.
Steve314
8

Vous demandez pourquoi ces langues n'ont pas été mises à jour pour inclure un récupérateur de place facultatif.

Le problème avec la récupération de place facultative est qu'il est impossible de mélanger du code utilisant les différents modèles. En d’autres termes, si j’écris du code qui suppose que vous utilisez un ramasse-miettes, vous ne pouvez pas l’utiliser dans votre programme pour lequel le ramasse-miettes est désactivé. Si vous le faites, ça va fuir partout.

Winston Ewert
la source
6

Pouvez-vous imaginer écrire un gestionnaire de périphérique dans une langue avec la récupération de place? Combien de bits peuvent arriver sur la ligne pendant le fonctionnement du GC?

Ou un système d'exploitation? Comment pouvez-vous démarrer la récupération de place en cours d'exécution avant même de démarrer le noyau?

C est conçu pour un niveau bas proche des tâches matérielles. Le problème? est-ce une langue si agréable que c'est un bon choix pour de nombreuses tâches de niveau supérieur. Les responsables des langues sont conscients de ces utilisations, mais ils doivent en priorité prendre en charge les exigences des pilotes de périphérique, du code intégré et des systèmes d'exploitation.

James Anderson
la source
2
C bon pour le haut niveau? J'ai sniffé mon verre partout sur mon clavier.
DeadMG
5
Eh bien, il a dit "beaucoup de tâches de niveau supérieur". Il pourrait compter les trolls (un, deux, beaucoup ...). Et il n'a pas réellement dit plus haut que quoi. Blagues mises à part, cependant, c’est vrai - la preuve est que beaucoup de projets importants de niveau supérieur ont été développés avec succès en C. Il existe peut-être de meilleurs choix pour bon nombre de ces projets, mais un projet en cours est une preuve plus solide que des spéculations sur ce qui pourrait a été.
Steve314
Il existe des systèmes d'exploitation gérés, et ils fonctionnent plutôt bien. En fait, lorsque vous rendez le système entier géré, les performances de l’utilisation du code géré chutent encore plus, jusqu’à aller plus vite que le code non géré dans des scénarios réels. Bien sûr, ce sont tous des "systèmes d'exploitation de recherche" - il n'y a pratiquement aucun moyen de les rendre compatibles avec le code non géré existant, à part la création d'un système d'exploitation non géré entièrement virtualisé dans le système d'exploitation géré. Microsoft a toutefois suggéré à un moment donné de remplacer Windows Server par l'un de ceux-ci, car de plus en plus de code serveur est écrit sur .NET.
Luaan
6

La réponse brève et ennuyeuse à cette question est qu'il doit exister un langage non récupéré pour les personnes qui écrivent les éboueurs. Il n’est pas facile d’un point de vue conceptuel d’avoir un langage qui permette en même temps un contrôle très précis de la structure de la mémoire et sur lequel se trouve un CPG.

L'autre question est de savoir pourquoi C et C ++ n'ont pas de récupérateur de place. Eh bien, je sais que le C ++ en a quelques-uns, mais ils ne sont pas très populaires car ils sont obligés de traiter avec un langage qui n'a pas été conçu pour être GC-ed, et les personnes qui l'utilisent encore cet âge n'est pas vraiment le genre qui manque un GC.

De plus, au lieu d’ajouter GC à un ancien langage non GC-ed, il est en fait plus facile de créer un nouveau langage qui utilise la même syntaxe tout en prenant en charge un GC. Java et C # en sont de bons exemples.

réduction
la source
1
Quelque part dans programmers.se ou SO, une affirmation selon laquelle quelqu'un travaillait sur une machine auto-amorçante ramassée par les ordures (IIRC) implémentait essentiellement la VM en utilisant un langage GC, avec un sous-ensemble d'amorçage utilisé pour implémenter le GC lui-même. J'ai oublié le nom. Lorsque j'ai examiné la question, il s'est avéré qu'ils n'avaient en principe jamais réalisé le saut du niveau sous-ensemble sans GC au niveau de travail du GC. Ceci est possible en principe, mais, autant que je sache, cela n'a jamais été réalisé - il s'agit certainement de faire les choses à la dure.
Steve314
@ Steve314: J'aimerais voir cela si vous vous rappelez jamais où vous l'avez trouvé!
Hugomg
trouvé le! Voir les commentaires à stackoverflow.com/questions/3317329/… en vous référant à la machine virtuelle Klein. Une partie du problème pour le trouver - la question était close.
Steve314
BTW - je semble incapable de commencer mes commentaires avec @missingno - que donne?
Steve314
@ steve314: Ayant reçu la réponse à cette discussion, je reçois déjà une notification pour tous les commentaires. Faire un @-post dans ce cas serait redondant et n'est pas autorisé par SE (ne me demandez pas pourquoi cependant). (La vraie cause est parce que mon numéro est manquant)
hugomg
5

Il y a divers problèmes, y compris ...

  • Bien que GC ait été inventé avant C ++, et peut-être avant C, C et C ++ ont tous deux été implémentés avant que les GC ne soient largement acceptés comme pratiques.
  • Vous ne pouvez pas facilement implémenter un langage et une plateforme GC sans un langage sous-jacent non GC.
  • Bien que le GC soit manifestement plus efficace que le non-GC pour les applications types développées à des échelles de temps typiques, etc., il existe des problèmes pour lesquels un effort supplémentaire de développement est un bon compromis et une gestion de la mémoire spécialisée surpassera un GC polyvalent. De plus, le C ++ est généralement plus efficace que la plupart des langages du GC, même sans effort de développement supplémentaire.
  • GC n’est pas universellement plus sûr que RAII de type C ++. RAII permet de nettoyer automatiquement des ressources autres que la mémoire, principalement parce qu'elle prend en charge des destructeurs fiables et rapides. Celles-ci ne peuvent pas être combinées avec les méthodes GC conventionnelles en raison de problèmes liés aux cycles de référence.
  • Les langages GC ont leurs propres types caractéristiques de fuites de mémoire, en particulier concernant les mémoires qui ne seront plus jamais utilisées, mais dans les cas où il existait des références existantes qui n'ont jamais été annulées ou remplacées. La nécessité de le faire explicitement n'est pas différente en principe de la nécessité deleteou freeexplicite. L’approche GC présente toujours un avantage - aucune référence en suspens - et l’analyse statique peut identifier certains cas, mais là encore, il n’existe pas de solution parfaite pour tous les cas.

Fondamentalement, cela concerne en partie l'âge des langues, mais il y aura toujours une place pour les langues autres que le GC, même si c'est un peu un créneau. Et sérieusement, en C ++, le manque de GC n'est pas un problème: votre mémoire est gérée différemment, mais elle n'est pas non gérée.

C ++ géré par Microsofts a au moins une certaine capacité à mélanger GC et non GC dans la même application, ce qui permet de combiner les avantages de chacun, mais je n'ai pas l'expérience nécessaire pour dire si cela fonctionne bien dans la pratique.

Rep-whoring liens vers des réponses de la mienne ...

Steve314
la source
4

Le ramassage des ordures est fondamentalement incompatible avec un langage système utilisé pour développer des pilotes pour du matériel compatible DMA.

Il est tout à fait possible que le seul pointeur sur un objet soit stocké dans un registre matériel de certains périphériques. Puisque le ramasse-miettes ne le saurait pas, il penserait que l'objet est inaccessible et le récupérera.

Cet argument est double pour le compactage de GC. Même si vous aviez pris soin de conserver les références en mémoire aux objets utilisés par les périphériques matériels, lorsque le CPG avait déplacé l'objet, il ne savait pas comment mettre à jour le pointeur contenu dans le registre de configuration des périphériques.

Vous avez donc besoin d'un mélange de tampons DMA immobiles et d'objets gérés par GC, ce qui signifie que vous avez tous les inconvénients des deux.

Ben Voigt
la source
On peut dire que tous les inconvénients des deux, mais moins d'instances de chaque inconvénient, et le même pour les avantages. Il est clair qu'il est complexe d'avoir plus de types de gestion de la mémoire à gérer, mais il peut également être évité la complexité en choisissant le bon cheval pour chaque parcours dans votre code. Peu probable, j'imagine, mais il existe un écart théorique. J'ai déjà envisagé de mélanger GC et non-GC dans le même langage, mais pas pour les pilotes de périphériques. Plutôt pour une application principalement GC, mais avec certaines bibliothèques de structures de données de bas niveau gérées manuellement par la mémoire.
Steve314
@ Steve314: Ne diriez-vous pas que se souvenir des objets qui doivent être libérés manuellement est aussi onéreux que de se souvenir de tout libérer? (Bien sûr, les pointeurs intelligents peuvent vous aider avec l'un ou l'autre, ce n'est donc pas un gros problème). Et vous avez besoin de pools différents pour les objets gérés manuellement par rapport aux objets rassemblés / compactables, car le compactage ne fonctionne pas bien lorsqu'il y a des objets fixes dispersés. Donc, beaucoup de complexité supplémentaire pour rien.
Ben Voigt
2
Pas s'il y a un clivage clair entre le code de haut niveau qui est tout le GC, et le code de bas niveau qui sort du GC. J'ai principalement développé l'idée en regardant D il y a quelques années, ce qui vous permet de vous retirer de GC mais ne vous permet pas de revenir en arrière. Prenons, par exemple, une bibliothèque d'arbres B +. Le conteneur dans son ensemble doit être GC, mais les nœuds de structure de données ne le sont probablement pas: il est plus efficace de réaliser une analyse personnalisée sur les nœuds terminaux uniquement, mais de demander au CPG d'effectuer une recherche récursive sur les nœuds de branche. Toutefois, cette analyse doit signaler les éléments contenus au GC.
Steve314
Le fait est que c'est une fonctionnalité contenue. Traiter les B + nœuds d'arbres que la gestion de la mémoire spéciale est WRT pas différent de les traiter comme WRT spéciale étant B + nœuds d'arbres. C'est une bibliothèque encapsulée, et le code de l'application n'a pas besoin de savoir que le comportement du GC a été contourné / mis en casse spéciale. Sauf que, du moins à l'époque, c'était impossible en D - comme je l'ai dit, aucun moyen de revenir en arrière et de signaler les éléments contenus au GC en tant que racines potentielles du GC.
Steve314
3

Parce que C & C ++ sont des langages de niveau relativement bas destinés à un usage général, même pour, par exemple, fonctionner sur un processeur 16 bits avec 1 Mo de mémoire dans un système embarqué, qui ne pouvait se permettre de gaspiller de la mémoire avec gc.

Petruza
la source
1
"Système embarqué"? À l'époque de la normalisation de C (1989), il devait être capable de gérer des PC avec 1 Mo de mémoire.
dan04
Je suis d'accord, je citais un exemple plus actuel.
Petruza
1 Mo ??? Bon Dieu, qui aurait jamais besoin de tant de RAM? </ billGates>
Mark K Cowan Le
2

Il existe des garbage collectors en C ++ et C. Vous ne savez pas comment cela fonctionne en C, mais en C ++, vous pouvez utiliser RTTI pour détecter dynamiquement votre graphe d'objets et l'utiliser pour le garbage collection.

À ma connaissance, vous ne pouvez pas écrire en Java sans un ramasse-miettes. Une petite recherche a révélé ceci .

La principale différence entre Java et C / C ++ est qu’en C / C ++, le choix vous appartient toujours, alors qu’en Java, les options sont souvent laissées de côté.

back2dos
la source
Et aussi que les éboueurs dédiés sont mieux implémentés, plus efficaces et s’intègrent mieux dans la langue. :)
Max
Non, vous ne pouvez pas utiliser RTTI pour découvrir dynamiquement le graphe d'objets en C / C ++: ce sont les anciens objets de données qui gâchent tout. Il n'y a tout simplement aucune information RTTI stockée dans un ancien objet de données ordinaire qui permettrait à un récupérateur de mémoire de faire la différence entre les pointeurs et les non-pointeurs au sein de cet objet. Pire encore, les pointeurs n'ont pas besoin d'être parfaitement alignés sur tout le matériel. Par conséquent, pour un objet de 16 octets, il existe 9 emplacements possibles où un pointeur de 64 bits peut être stocké, dont deux seulement ne se chevauchent pas.
cmaster
2

C'est un compromis entre performance et sécurité.

Il n’existe aucune garantie que vos déchets soient collectés en Java; ils risquent donc de perdre du temps à utiliser de l’espace, tandis que l’analyse des objets non référencés (par exemple, des déchets) prend plus de temps que la suppression ou la libération explicite d’un objet inutilisé.

Bien entendu, l’avantage est que l’on peut construire un langage sans pointeurs ni fuites de mémoire, ce qui permet de produire un code correct.

Il peut y avoir parfois un léger avantage «religieux» dans ces débats - soyez prévenus!

adrianmcmenamin
la source
1

Voici une liste de problèmes inhérents au GC, qui le rendent inutilisable dans un langage système tel que C:

  • Le CPG doit être inférieur au niveau du code dont il gère les objets. Il n'y a tout simplement pas un tel niveau dans un noyau.

  • Un GC doit arrêter le code géré de temps en temps. Maintenant, réfléchissez à ce qui se passerait si cela causait cela à votre noyau. Tous les traitements sur votre machine s’arrêteraient pendant une milliseconde, par exemple, pendant que le CPG analyse toutes les allocations de mémoire existantes. Cela tuerait toutes les tentatives visant à créer des systèmes fonctionnant dans des conditions strictes en temps réel.

  • Un GC doit pouvoir faire la distinction entre les pointeurs et les non-pointeurs. C'est-à-dire qu'il doit pouvoir examiner chaque objet de la mémoire existant et pouvoir produire une liste de décalages où ses pointeurs peuvent être trouvés.

    Cette découverte doit être parfaite: le GC doit pouvoir chasser tous les indicateurs qu’il découvre. Si elle déréférencait un faux positif, elle risquerait de planter. Si elle ne parvenait pas à détecter un faux négatif, elle détruirait probablement un objet toujours en cours d'utilisation, en provoquant une panne du code géré ou une corruption silencieuse de ses données.

    Cela nécessite absolument que les informations de type soient stockées dans chaque objet existant. Cependant, C et C ++ autorisent tous deux les anciens objets de données qui ne contiennent aucune information de type.

  • Le GC est une entreprise intrinsèquement lente. Les programmeurs qui ont été socialisés avec Java peuvent ne pas s'en rendre compte, mais les programmes peuvent être beaucoup plus rapides s'ils ne sont pas implémentés en Java. Et l'un des facteurs qui ralentissent Java est le GC. C'est ce qui empêche les langages GCed tels que Java d'être utilisés en superinformatique. Si votre machine coûte un million par an en consommation d'énergie, vous ne voulez pas payer même 10% de cela pour la collecte des ordures.

C et C ++ sont des langages créés pour prendre en charge tous les cas d'utilisation possibles. Et, comme vous le voyez, nombre de ces cas d'utilisation sont exclus du nettoyage de la mémoire. Ainsi, afin de prendre en charge ces cas d'utilisation, C / C ++ ne peut pas être nettoyé.

cmaster
la source