Pourquoi le ramassage des ordures si les pointeurs intelligents sont là

67

De nos jours, tant de langues sont ramassées. Il est même disponible pour C ++ par des tiers. Mais C ++ a RAII et des pointeurs intelligents. Alors, quel est l'intérêt d'utiliser la récupération de place? Est-ce qu'il fait quelque chose de plus?

Et dans d'autres langages comme C #, si toutes les références sont traitées comme des pointeurs intelligents (en gardant RAII de côté), par spécification et par implémentation, y aura-t-il encore besoin de récupérateurs de place? Si non, alors pourquoi n'est-ce pas?

Gulshan
la source
1
Une chose que j'ai comprise après avoir posé cette question: les pointeurs intelligents ont besoin de RAII pour gérer la désallocation automatique .
Gulshan
8
Les pointeurs intelligents signifient que l’on utilise RAII pour la GC;)
Dario
Heh, c # devrait avoir une option pour gérer tout le "ramassage des ordures" avec RAII. Les références circulaires peuvent être détectées à l’arrêt de l’application; il suffit de voir quelles allocations sont encore en mémoire après la libération de Program.cs-class. Ensuite, les références circulaires peuvent être remplacées par une sorte de référence hebdomadaire.
Août
4
Une réponse
Gulshan le

Réponses:

67

Alors, quel est l'intérêt d'utiliser la récupération de place?

Je suppose que vous voulez dire que les pointeurs intelligents comptés en référence sont des formes (rudimentaires) de récupération des ordures; je vais donc répondre à la question "quels sont les avantages des autres formes de récupération des ordures par rapport aux pointeurs intelligents comptés en référence" au lieu.

  • Précision . Le comptage de références seul fuit les cycles; ainsi, les pointeurs intelligents comptés de références perdront de la mémoire en général, à moins que d'autres techniques ne soient ajoutées aux cycles de capture. Une fois ces techniques ajoutées, le bénéfice de la simplicité du comptage de références a disparu. Notez également que les GC de comptage et de suivi de références basés sur la portée collectent des valeurs à des moments différents, parfois le comptage de références est collecté plus tôt et parfois, les GC de traçage collectés plus tôt.

  • Débit . Les pointeurs intelligents sont l’une des formes de récupération de place les moins efficaces, en particulier dans le contexte d’applications multithreads lorsque les décomptes de références sont modifiés de manière atomique. Il existe des techniques avancées de comptage de références conçues pour remédier à ce problème, mais le traçage des GC reste l'algorithme de choix dans les environnements de production.

  • Latence . Les implémentations classiques de pointeurs intelligents permettent aux destructeurs de se déclencher, ce qui entraîne des temps de pause illimités. D'autres formes de ramassage des ordures sont beaucoup plus progressives et peuvent même se faire en temps réel, par exemple le tapis de course de Baker.

Jon Harrop
la source
23
Je ne peux pas croire que cette réponse était la première réponse. Cela montre un manque total de compréhension des pointeurs intelligents C ++ et fait des affirmations tellement décalées par rapport à la réalité que c'est tout simplement ridicule. Premièrement, le pointeur intelligent qui dominera dans un morceau de code C ++ bien conçu est le pointeur unique, pas le pointeur des partages. fr.cppreference.com/w/cpp/memory/unique_ptr Et, deuxièmement, je ne peux pas croire que vous revendiquez réellement des avantages de «performances» et des avantages en temps réel de la récupération de place non déterministe par rapport aux pointeurs intelligents.
user1703394
4
@ user1703394 Il semble que le répondeur avait en tête des pointeurs partagés (à tort ou à raison, je ne sais pas trop ce que suggèrent les PO), qui sont moins performants que la récupération de place non déterministe.
Nathan Cooper
8
Ce sont tous des arguments de l'homme de paille qui ne sont valables que si vous ignorez complètement la question ou si vous ignorez les modèles d'utilisation actuels des différents types de pointeurs intelligents. La question portait sur les pointeurs intelligents. Oui shared_ptr est un pointeur intelligent et oui shared_ptr est le plus cher des pointeurs intelligents, mais non, il n’existe aucun argument en faveur de leur utilisation généralisée qui soit proche de la pertinence d’un argument de performance. Sérieusement, cette réponse devrait être déplacée à une question sur le comptage des références. C’est une référence médiocre qui compte la réponse à une bonne question de pointeur intelligent.
user1703394
4
"Les pointeurs intelligents ne sont pas un concept plus large", Sérieusement? Vous ne savez pas à quel point cette affirmation mine tous les arguments éventuellement valables que vous avez avancés jusqu'à présent. Jetez peut-être un coup d'œil à la propriété de Rust et à la sémantique du déplacement: koerbitz.me/posts/… Il est facile pour les personnes ayant une expérience historique avec C ++ de passer à côté du fait que C ++ 11 / C ++ 14 avec son modèle de mémoire, amélioré la sémantique des pointeurs et des mouvements est une toute autre bête que leurs prédécesseurs. Regardez comment Rust le fait, il est plus propre que le C ++ et offre une perspective nouvelle.
user1703394
6
@ user1703394: "Les pauses non liées dues aux destructeurs sont une propriété malheureuse de RAII utilisée pour des ressources autres que la mémoire". Non, cela n'a rien à voir avec des ressources non-mémoire.
Jon Harrop
63

Étant donné que personne n’a examiné la question sous cet angle, je reformulerai votre question: pourquoi mettre quelque chose dans le langage si vous pouvez le faire dans une bibliothèque? Ignorant des détails spécifiques à l’implémentation et à la syntaxe, GC / pointeurs intelligents est fondamentalement un cas particulier de cette question. Pourquoi définir un ramasse-miettes dans le langage lui-même si vous pouvez l'implémenter dans une bibliothèque?

Il y a quelques réponses à cette question. Le plus important en premier:

  1. Vous vous assurez que tout le code peut l'utiliser pour interagir. C’est, à mon avis, la principale raison pour laquelle la réutilisation et le partage de code n’a pas vraiment pris son essor avant Java / C # / Python / Ruby. Les bibliothèques ont besoin de communiquer, et leur seul langage partagé fiable est ce qu’elles contiennent (et dans une certaine mesure, leur bibliothèque standard). Si vous avez déjà essayé de réutiliser des bibliothèques en C ++, vous avez probablement subi la douleur affreuse qu'aucune sémantique de mémoire standard ne provoque. Je veux passer une struct à une lib. Est-ce que je passe une référence? Aiguille? scoped_ptr?smart_ptr? Est-ce que je passe la propriété, ou pas? Y a-t-il un moyen de l'indiquer? Que faire si la lib doit allouer? Dois-je lui donner un allocateur? En ne faisant pas de la gestion de la mémoire une partie intégrante du langage, le C ++ oblige chaque paire de bibliothèques à négocier leur propre stratégie spécifique ici, et il est vraiment difficile de les mettre tous d'accord. GC en fait un non-problème complet.

  2. Vous pouvez concevoir la syntaxe autour de cela. Comme C ++ n'encapsule pas la gestion de la mémoire elle-même, il doit fournir une gamme de points d'ancrage syntaxiques pour permettre au code de niveau utilisateur d'exprimer tous les détails. Vous avez des pointeurs, des références, des constopérateurs de déréférencement, des opérateurs d'indirection, des adresses, etc. Si vous transférez la gestion de la mémoire dans le langage même, la syntaxe peut être conçue autour de cela. Tous ces opérateurs disparaissent et le langage devient plus propre et plus simple.

  3. Vous obtenez un retour sur investissement élevé. La valeur générée par un morceau de code donné est multipliée par le nombre de personnes l'utilisant. Cela signifie que plus vous avez d'utilisateurs, plus vous pouvez vous permettre d'acheter un logiciel. Lorsque vous déplacez une fonctionnalité dans la langue, tous les utilisateurs de la langue l'utilisent. Cela signifie que vous pouvez y consacrer plus d’efforts qu’une bibliothèque uniquement utilisée par un sous-ensemble de ces utilisateurs. C'est pourquoi les langages tels que Java et C # ont des machines virtuelles de premier ordre et des éboueurs d'une qualité fantastique: le coût de leur développement est amorti par des millions d'utilisateurs.

munificent
la source
Réponse fantastique! Si seulement je pouvais voter plus d'une fois ...
Dean Harding Le
10
Il est à noter que la récupération de place n'est pas réellement implémentée dans le langage C #, mais dans le .NET Framework , plus précisément le Common Language Runtime (CLR).
Robert Harvey
6
@RobertHarvey: Ce n'est pas implémenté par le langage, mais cela ne fonctionnerait pas sans la coopération du langage. Par exemple, le compilateur doit inclure des informations spécifiant, à chaque point du code, l'emplacement de chaque registre ou offset de pile qui contient une référence à un objet non épinglé. C'est un invariant absolu sans exception qui ne pourrait être maintenu sans support linguistique.
Supercat
Le fait que le GC prenne en charge le langage et le cadre requis présente l’un des principaux avantages: il garantit qu’aucune référence à la mémoire, qui pourrait être allouée à une autre fin, n’existera jamais. Si l'on appelle Disposeun objet qui encapsule une bitmap, toute référence à cet objet sera une référence à un objet bitmap supprimé. Si l'objet a été supprimé prématurément alors qu'un autre code s'attend toujours à être utilisé, la classe bitmap peut garantir que l'autre code échouera de manière prévisible . En revanche, l'utilisation d'une référence à la mémoire libérée est définie par le comportement non défini.
Supercat
34

La récupération de place signifie simplement que vos objets alloués sont automatiquement libérés à un moment donné après qu’ils ne sont plus joignables.

Plus précisément, ils sont libérés lorsqu'ils deviennent inaccessibles pour le programme, car les objets référencés de manière circulaire ne seraient jamais libérés autrement.

Les pointeurs intelligents font simplement référence à toute structure qui se comporte comme un pointeur ordinaire, mais qui comporte des fonctionnalités supplémentaires. Ceux - ci incluent, mais ne sont pas limités à la désallocation, mais également à la copie sur écriture, aux contrôles liés, ...

Maintenant, comme vous l'avez dit, les pointeurs intelligents peuvent être utilisés pour implémenter une forme de récupération de place.

Mais le train de pensée va de la manière suivante:

  1. La collecte des ordures ménagères est une bonne chose, car c'est pratique et je dois m'occuper de moins de choses
  2. Par conséquent: je veux le ramassage des ordures dans ma langue
  3. Maintenant, comment obtenir GC dans ma langue?

Bien sûr, vous pouvez le concevoir comme ça depuis le début. C # a été conçu pour être récupéré, newil ne reste donc que votre objet et il sera publié lorsque les références tomberont hors de portée. La façon dont cela est fait est à la charge du compilateur.

Mais en C ++, il n'y avait pas de récupération de place prévue. Si nous allouons un pointeur int* p = new int;qui tombe en dehors de la portée, il pest lui-même retiré de la pile, mais personne ne s'occupe de la mémoire allouée.

Désormais, les destructeurs déterministes sont la seule chose que vous ayez au départ . Lorsqu'un objet quitte l'étendue dans laquelle il a été créé, son destructeur est appelé. En combinaison avec des modèles et une surcharge d'opérateur, vous pouvez concevoir un objet wrapper qui se comporte comme un pointeur, mais utilise la fonctionnalité de destructeur pour nettoyer les ressources qui lui sont attachées (RAII). Vous appelez celui-ci un pointeur intelligent .

Tout cela est très spécifique à C ++: surcharge d’opérateur, modèles, destructeurs, ... Dans cette situation de langage particulière, vous avez développé des pointeurs intelligents pour vous fournir le GC souhaité.

Mais si vous concevez une langue avec GC dès le départ, il ne s'agit que d'un détail d'implémentation. Vous dites simplement que l'objet sera nettoyé et que le compilateur le fera pour vous.

Des pointeurs intelligents comme en C ++ ne seraient probablement même pas possibles dans des langages comme C #, qui n’ont aucune destruction déterministe (C # contourne cela en fournissant du sucre syntaxique permettant d’appeler a .Dispose()sur certains objets). Les ressources non référencées seront finalement récupérées par le GC, mais on ne sait pas exactement quand cela se produira.

Et ceci, à son tour, peut permettre au GC de faire son travail plus efficacement. Intégré plus profondément dans le langage que les pointeurs intelligents, le GC GC .NET peut par exemple retarder des opérations de mémoire et les exécuter par blocs pour les rendre moins chères ou même déplacer la mémoire pour une efficacité accrue en fonction de la fréquence des objets sont consultés.

Dario
la source
C # a une forme de destruction déterministe via IDisposableet using. Mais cela demande un peu d’effort de la part des programmeurs, raison pour laquelle il n’est généralement utilisé que pour des ressources très rares telles que les descripteurs de connexion à une base de données.
JSB
7
@JSBangs: Exactement. Tout comme C ++ construit des pointeurs intelligents autour de RAII pour obtenir GC, C # va dans l'autre sens et construit des "dispositifs intelligents" autour du GC pour obtenir RAII;) En fait, il est dommage que RAII soit si difficile en C # car c'est génial pour une exception- traitement sûr des ressources. F # essaye par exemple une IDisposablesyntaxe plus simple en remplaçant simplement conventionnelle let ident = valuepar use ident = value...
Dario
@Dario: "Le C # va dans l'autre sens et construit des" dispositifs intelligents "autour du GC pour obtenir le RAII". RAII en C # avec usingn'a rien à voir avec la récupération de place, il appelle simplement une fonction lorsqu'une variable tombe en dehors de la portée, comme les destructeurs en C ++.
Jon Harrop
1
@Jon Harrop: S'il vous plaît quoi? L’instruction que vous citez concerne les destructeurs simples C ++, sans aucun décompte de références / pointeurs intelligents / récupération de place impliqués.
Dario
1
"La récupération de place signifie simplement que vos objets alloués sont automatiquement libérés lorsqu'ils ne sont plus référencés. Plus précisément, ils sont libérés lorsqu'ils deviennent inaccessibles pour le programme, car les objets référencés de manière circulaire ne seraient jamais libérés autrement." ... Plus précis serait de dire qu'ils sont automatiquement publiés à un moment donné après , pas quand . Notez que lorsque cela implique que la récupération a lieu immédiatement, alors que la récupération a souvent lieu beaucoup plus tard.
Todd Lehman
4

À mon sens, il existe deux grandes différences entre la récupération de place et les pointeurs intelligents utilisés pour la gestion de la mémoire:

  1. Les pointeurs intelligents ne peuvent pas collecter les déchets cycliques; la collecte des ordures peut
  2. Les pointeurs intelligents effectuent tout le travail au moment du référencement, du déréférencement et de la désallocation, sur le thread d'application; la collecte des ordures n'a pas besoin

Le premier signifie que GC récupérera des ordures que les indicateurs intelligents ne feront pas; Si vous utilisez des pointeurs intelligents, vous devez éviter de créer ce type de déchets ou être prêt à les gérer manuellement.

Ce dernier signifie que, peu importe la façon dont les pointeurs intelligents sont intelligents, leur fonctionnement ralentira les tâches en cours dans votre programme. Le ramassage des ordures peut différer le travail et le déplacer vers d'autres threads; cela lui permet d'être globalement plus efficace (en effet, le coût d'exécution d'un GC moderne est inférieur à celui d'un système malloc / free normal, même sans la surcharge des pointeurs intelligents), et de faire le travail qu'il reste à faire sans entrer dans le manière des threads de l'application.

Notez maintenant que les pointeurs intelligents, en tant que constructions programmatiques, peuvent être utilisés pour faire toutes sortes d'autres choses intéressantes - voir la réponse de Dario - qui sont complètement hors de la portée du garbage collection. Si vous voulez faire cela, vous aurez besoin de pointeurs intelligents.

Cependant, aux fins de la gestion de la mémoire, je ne vois aucune possibilité de remplacer des pointeurs intelligents par le nettoyage de la mémoire. Ils ne sont tout simplement pas aussi bons à ça.

Tom Anderson
la source
6
@Tom: jetez un coup d'œil à la réponse de Dario pour plus de détails sur les pointeurs intelligents. En ce qui concerne les avantages des pointeurs intelligents - la désallocation déterministe peut être un avantage énorme lorsqu’elle est utilisée pour contrôler des ressources (pas seulement de la mémoire). En fait, cela s’est avéré si important que Microsoft a introduit le usingbloc dans les versions ultérieures de C #. De plus, le comportement non déterministe des GC peut être interdit dans les systèmes en temps réel (c’est pourquoi les GC ne sont pas utilisés là-bas). De plus, n'oublions pas que les GC sont si complexes à résoudre que la plupart perdent réellement de la mémoire et sont assez inefficaces (par exemple, Boehm…).
Konrad Rudolph
6
Je pense que le non-déterminisme des GC est un peu déroutant: il existe des systèmes de GC adaptés à une utilisation en temps réel (comme IBM Recycler), même si ceux que vous voyez dans les ordinateurs virtuels de bureau et de serveur ne le sont pas. De plus, utiliser des pointeurs intelligents signifie utiliser malloc / free, et les implémentations classiques de malloc sont non déterministes en raison de la nécessité de rechercher dans la liste libre. Les systèmes en mouvement du GC ont des temps d’affectation plus déterministes que les systèmes malloc / free, bien que les temps de désaffectation soient moins déterminants.
Tom Anderson
3
En ce qui concerne la complexité: oui, les GC sont complexes, mais je ne suis pas au courant que "la plupart des fuites de mémoire sont plutôt inefficaces", et je serais intéressé de voir des preuves du contraire. Boehm n'est pas une preuve, car c'est une implémentation très primitive, et elle est conçue pour servir un langage, le C, où la précision de la GC est fondamentalement impossible en raison du manque de sécurité de type. C'est un effort courageux, et ça marche du tout, c'est très impressionnant, mais vous ne pouvez pas le prendre pour un exemple de GC.
Tom Anderson
8
@Jon: décidément pas des conneries. bugzilla.novell.com/show_bug.cgi?id=621899 ou, plus généralement: flyingfrogblog.blogspot.com/2009/01/… C'est bien connu et une propriété de tous les GC conservateurs.
Konrad Rudolph
3
"Le coût d'exécution d'un CPG moderne est inférieur à celui d'un système malloc / free normal." Le hareng rouge ici. C'est uniquement parce que le malloc traditionnel est un algorithme extrêmement inefficace. Les allocateurs modernes qui utilisent plusieurs compartiments pour différentes tailles de bloc sont beaucoup plus rapides à allouer, beaucoup moins sujets à la fragmentation de tas et vous permettent néanmoins une désallocation rapide.
Mason Wheeler
3

Le terme collecte des ordures implique qu'il y a des ordures à ramasser. En C ++, les pointeurs intelligents se déclinent en plusieurs versions, en particulier unique_ptr. Unique_ptr est fondamentalement une propriété unique et une structure de portée. Dans un élément de code bien conçu, la plupart des ressources allouées aux segments de mémoire se trouveraient normalement derrière des pointeurs intelligents unique_ptr et la propriété de ces ressources serait toujours bien définie. Unique_ptr est pratiquement inutilisable et unique_ptr élimine la plupart des problèmes de gestion manuelle de la mémoire qui conduisaient traditionnellement les utilisateurs aux langues gérées. Maintenant que de plus en plus de cœurs fonctionnant simultanément deviennent de plus en plus courants, les principes de conception qui poussent le code à utiliser une propriété unique et bien définie à tout moment sont plus importants pour la performance.

Même dans un programme bien conçu, en particulier dans des environnements multithreads, tout ne peut pas être exprimé sans structures de données partagées, et pour les structures de données qui nécessitent réellement, les threads doivent communiquer. RAII en c ++ fonctionne assez bien pour les problèmes de durée de vie dans une configuration à un seul thread. Dans une configuration à plusieurs threads, la durée de vie des objets peut ne pas être complètement définie de manière hiérarchique. Pour ces situations, l'utilisation de shared_ptr offre une grande partie de la solution. Vous créez la propriété partagée d'une ressource et cela en C ++ est le seul endroit où nous voyons des ordures, mais à des quantités si petites qu'un programme c ++ conçu correctement devrait être considéré comme une implémentation de la collection 'litter' avec shared-ptr plutôt qu'une collecte à part entière mis en œuvre dans d'autres langues. C ++ n'a tout simplement pas beaucoup de "déchets"

Comme indiqué par d'autres, les pointeurs intelligents comptés en référence sont une forme de récupération de place, ce qui pose un problème majeur. L'exemple utilisé principalement comme inconvénient des formes de numérotation des références comptées par référence est le problème de la création de structures de données orphelines connectées à des pointeurs intelligents qui créent des clusters d'objets qui s'empêchent d'être collectés. Tandis que dans un programme conçu selon le modèle de calcul acteur, les structures de données ne permettent généralement pas à de tels clusters non récupérables d'apparaître en C ++, lorsque vous utilisez l'approche de données partagées large pour la programmation multithread, comme cela est utilisé principalement dans une grande partie de l'industrie, ces grappes d'orphelins peuvent rapidement devenir une réalité.

Donc, pour résumer, si par utilisation de pointeur partagé, vous entendez l'utilisation généralisée de unique_ptr associée au modèle d'acteur de l'approche de calcul pour la programmation multithread et l'utilisation limitée de shared_ptr, alors que les autres formes de récupération de place ne vous en achètent pas avantages supplémentaires. Si toutefois une approche tout partagé vous aboutissait avec shared_ptr partout, vous devriez envisager de changer de modèle de concurrence ou de passer à un langage géré plus adapté au partage plus large de la propriété et à l’accès simultané aux structures de données.

utilisateur1703394
la source
1
Cela signifie-t-il qu'il Rustn'est pas nécessaire de ramasser les ordures?
Gulshan
1
@Gulshan Rust est l'une des rares langues à prendre en charge des pointeurs uniques sécurisés.
CodesInChaos
2

La plupart des pointeurs intelligents sont implémentés à l'aide du comptage de références. Autrement dit, chaque pointeur intelligent qui fait référence à un objet incrémente le nombre de références des objets. Lorsque ce nombre passe à zéro, l'objet est libéré.

Le problème existe si vous avez des références circulaires. Autrement dit, A a une référence à B, B a une référence à C et C a une référence à A. Si vous utilisez des pointeurs intelligents, vous devez manuellement pour libérer la mémoire associée à A, B et C entrez une "rupture" dans la référence circulaire (en utilisant par exemple weak_ptrC ++).

Le ramassage des ordures (généralement) fonctionne assez différemment. La plupart des éboueurs de nos jours utilisent un test d'accessibilité . Autrement dit, il examine toutes les références de la pile et celles qui sont globalement accessibles, puis trace chaque objet auquel ces références se réfèrent, et les objets auxquels elles font référence, etc. Tout le reste est de la foutaise.

De cette manière, les références circulaires importent moins - tant que ni A, B ni C ne sont joignables , la mémoire peut être récupérée.

La "vraie" collecte des ordures offre d’autres avantages. Par exemple, l'allocation de mémoire est extrêmement économique: il suffit d'incrémenter le pointeur sur la "fin" du bloc de mémoire. La désallocation a également un coût amorti constant. Bien entendu, des langages tels que C ++ vous permettent d’implémenter la gestion de la mémoire à votre guise. Vous pouvez ainsi concevoir une stratégie d’allocation encore plus rapide.

Bien entendu, en C ++, la quantité de mémoire allouée aux segments de mémoire est généralement inférieure à un langage de référence lourd tel que C # / .NET. Mais ce n'est pas vraiment un problème de collecte de déchets contre les pointeurs intelligents.

Dans tous les cas, le problème n'est pas évident: l'un est meilleur que l'autre. Ils ont chacun des avantages et des inconvénients.

Dean Harding
la source
2

C'est une question de performance . L'annulation de la mémoire nécessite beaucoup d'administration. Si la suppression de l'allocation s'exécute en arrière-plan, les performances du processus de premier plan augmentent. Malheureusement, l'allocation de mémoire ne peut pas être paresseuse (les objets alloués seront utilisés à l'instant sacré), mais les objets libérés le peuvent.

Essayez en C ++ (sans GC) d’allouer un grand nombre d’objets, d’afficher «bonjour», puis de les supprimer. Vous serez surpris du temps nécessaire pour libérer des objets.

De plus, GNU libc fournit des outils plus efficaces pour désallouer de la mémoire, voir obstacks . Je dois remarquer que je n'ai aucune expérience d'obstacles, je ne les ai jamais utilisés.

ern0
la source
En principe, vous avez quelque chose à dire, mais il convient de noter qu’il s’agit d’un problème qui a une solution très simple: utilisez un allocateur de pool ou un allocateur de petit objet pour regrouper les désallocations. Mais cela demande certes plus (légèrement) plus d’efforts qu’un GC exécuté en arrière-plan.
Konrad Rudolph
Oui, bien sûr, GC est beaucoup plus confortable. (Surtout pour les débutants: il n'y a pas de problème de propriété, il n'y a même pas d'opérateur de suppression.)
ern0
3
@ ern0: non. Le point entier (pointage intelligent) des pointeurs intelligents est qu’il n’ya pas de problème de propriété ni d’opérateur de suppression.
Konrad Rudolph
3
@Jon: ce qui, honnêtement, est la plupart du temps. Si vous partagez volontiers l'état d'objet entre différents threads, vous aurez des problèmes complètement différents. J'admets que beaucoup de gens programment ainsi, mais c'est une conséquence des abstractions de threading qui existaient jusqu'à récemment et ce n'est pas une bonne façon de faire du multithreading.
Konrad Rudolph
1
La désaffectation n'est souvent pas effectuée "en arrière-plan", mais met en pause tous les threads de premier plan. La collecte de déchets en mode batch est généralement un gain de performances, malgré la pause des threads de premier plan, car elle permet de consolider l'espace non utilisé. On pourrait séparer les processus de collecte des ordures et de compactification de tas, mais - en particulier dans les cadres qui utilisent des références directes plutôt que des poignées - ils tendent tous deux à être des processus "arrêtant le monde", et il est souvent plus pratique de les faire ensemble.
Supercat
2

Le ramassage des ordures peut être plus efficace: il regroupe en gros la surcharge de la gestion de la mémoire et le fait en une fois. En général, cela signifie que moins de ressources processeur seront utilisées pour la désallocation de mémoire, mais cela signifiera que vous aurez une grosse activité de désallocation à un moment donné. Si le CPG n'est pas correctement conçu, il peut être visible par l'utilisateur sous la forme d'une "pause" pendant que le CPG tente de désallouer de la mémoire. La plupart des GC modernes sont très efficaces pour garder cette information invisible pour l'utilisateur, sauf dans les conditions les plus défavorables.

Les pointeurs intelligents (ou tout système de comptage de références) ont l'avantage de se produire exactement au moment où vous vous attendez à consulter le code (le pointeur intelligent est hors de portée, les éléments sont supprimés). Vous obtenez de petites quantités de désaffectation ici et là. Globalement, vous pouvez utiliser plus de temps processeur lors de la désaffectation, mais comme il est réparti sur tout ce qui se passe dans votre programme, il est moins probable (affichage désaffecté d'une structure de données monstre) visible pour votre utilisateur.

Si vous faites quelque chose où la réactivité est importante, je suggérerais que les compteurs / références intelligents vous permettent de savoir exactement quand les choses se passent, afin que vous puissiez savoir tout en codant ce qui est susceptible de devenir visible pour vos utilisateurs. Dans un contexte GC, vous n’avez que le contrôle le plus éphémère sur le ramasse-miettes et vous devez simplement essayer de contourner le problème.

D'autre part, si votre objectif est d'obtenir un débit global, un système basé sur le GC peut constituer un bien meilleur choix, car il réduit les ressources nécessaires à la gestion de la mémoire.

Cycles: je ne considère pas que le problème des cycles soit important. Dans un système où vous avez des pointeurs intelligents, vous avez tendance à privilégier les structures de données qui ne comportent pas de cycles, ou vous faites simplement attention à la façon dont vous laissez aller ces choses. Si nécessaire, vous pouvez utiliser des objets conservateurs sachant briser les cycles des objets possédés afin d'assurer automatiquement la destruction appropriée. Dans certains domaines de la programmation, cela peut être important, mais pour la plupart des tâches quotidiennes, ce n'est pas pertinent.

Michael Kohne
la source
1
"vous aurez un grand sursaut d'activité de désallocation à un moment donné". Le tapis roulant de Baker est un exemple de récupérateur de déchets magnifiquement incrémental. memorymanagement.org/glossary/t.html#treadmill
Jon Harrop
1

La limitation numéro un des pointeurs intelligents est qu’ils n’aident pas toujours les références circulaires. Par exemple, vous avez un objet A qui stocke un pointeur intelligent sur un objet B et un objet B stocke un pointeur intelligent sur un objet A. S'ils sont laissés ensemble sans réinitialiser aucun des pointeurs, ils ne seront jamais désalloués.

Cela se produit car un pointeur intelligent doit effectuer une action spécifique qui ne sera pas triigrée dans le scénario ci-dessus car les deux objets sont inaccessibles au programme. Le ramassage des ordures fera face - il identifiera correctement que les objets ne sont pas accessibles au programme et ils seront collectés.

en dents de scie
la source
1

C'est un spectre .

Si vous ne voulez pas que les performances soient serrées et que vous êtes prêt à vous lancer dans la mouture, vous vous retrouverez au montage ou à l'assemblage, avec tout le fardeau qui vous incombe de prendre les bonnes décisions et toute la liberté de le faire, mais avec , toute la liberté de tout gâcher:

"Je vais te dire quoi faire, tu le fais. Fais-moi confiance".

Le ramassage des ordures est l’autre extrémité du spectre. Vous avez très peu de contrôle, mais sa prise en charge pour vous:

"Je vais vous dire ce que je veux, vous y arrivez".

Cela présente de nombreux avantages, notamment le fait que vous n'avez pas besoin d'être aussi digne de confiance lorsqu'il s'agit de savoir exactement quand une ressource n'est plus nécessaire, mais (malgré certaines des réponses qui circulent ici), la performance n'est pas bonne, et la prévisibilité des performances. (Comme toutes les choses, si vous avez le contrôle et que vous faites quelque chose de stupide, vous pouvez avoir de pires résultats. Cependant, suggérer que savoir au moment de la compilation quelles sont les conditions pour pouvoir libérer de la mémoire, ne peut pas être utilisé comme une performance gagnante, c’est au-delà naïf).

RAII, cadrage, comptage d'arbitrage, etc. sont tous des aides pour vous permettre d'aller plus loin dans ce spectre, mais ce n'est pas tout le chemin. Toutes ces choses nécessitent encore une utilisation active. Ils vous permettent toujours d'interagir avec la gestion de la mémoire de la même manière que la récupération de place.

droguer
la source
0

S'il vous plaît rappelez-vous qu'à la fin, tout se résume à un CPU exécutant des instructions. À ma connaissance, tous les processeurs grand public ont des jeux d’instructions qui nécessitent que les données soient stockées à un endroit donné en mémoire et que vous disposiez de pointeurs vers ces données. C'est tout ce que vous avez au niveau de base.

Tout ce qui s’y rattache avec la récupération de place, les références aux données qui ont pu être déplacées, le compactage de tas, etc., fait le travail dans les limites données par le paradigme ci-dessus "bloc de mémoire avec un pointeur d’adresse". Même chose avec les pointeurs intelligents - vous devez ENCORE faire exécuter le code sur du matériel réel.


la source