Pour ajouter au débat ici.
Il y a des problèmes connus avec la collecte des ordures, et de les comprendre permet de comprendre pourquoi il n'y a aucune en C++.
1. La Performance ?
La première plainte est souvent question de performance, mais la plupart des gens n'ont pas vraiment conscience de ce qu'ils sont en train de parler. Comme l'illustre Martin Beckett
le problème peut ne pas être la performance en soi, mais la prévisibilité de la performance.
Actuellement, il existe 2 familles de GC, qui sont largement déployés:
- Mark-and-Sweep type
- Référence de Comptage de type
L' Mark And Sweep
est plus rapide (moins d'impact sur les performances d'ensemble) mais il souffre d'un "gel du monde" syndrome: c'est à dire lorsque le GC coups de pied dans, tout le reste est arrêté jusqu'à ce que le GC a fait son nettoyage. Si vous souhaitez créer un serveur qui répond en quelques millisecondes... certaines transactions ne sera pas à la hauteur de vos attentes :)
Le problème de la Reference Counting
est différent: comptage de références ajoute-dessus, surtout en Multi-Threading environnements parce que vous devez avoir atomique comte. En outre il y a le problème des cycles de référence si vous avez besoin d'un algorithme intelligent pour détecter les cycles et les éliminer (généralement mise en œuvre par le "gel du monde" aussi, bien que moins fréquent). En général, à compter d'aujourd'hui, ce genre (même si normalement plus réactif, ou plutôt, le gel de moins en moins souvent) est plus lent que l' Mark And Sweep
.
J'ai vu un papier par Eiffel maîtres d'œuvre qui ont essayé de mettre en œuvre un Reference Counting
Garbage Collector qui sont similaires performance globale d' Mark And Sweep
sans le "Gel Du Monde". Il faut un thread séparé pour le GC (typique). L'algorithme a été un peu effrayant (à la fin), mais le papier fait un bon travail de l'introduction des concepts à la fois et en montrant l'évolution de l'algorithme de la "simple" version à part entière. Lecture recommandée si seulement je pouvais mettre ma main sur le fichier PDF...
2. L'Acquisition De Ressources Est D'Initialisation
Il s'agit d'un idiome en C++
que vous enveloppez-la propriété des ressources à l'intérieur d'un objet pour s'assurer qu'ils sont correctement libérées. Il est principalement utilisé pour la mémoire puisque nous n'avons pas de collecte des ordures, mais il est aussi très utile quand même pour beaucoup d'autres situations:
- les verrous (multi-thread, descripteur de fichier, ...)
- connexions (pour une base de données, un autre serveur, ...)
L'idée est de bien contrôler la durée de vie de l'objet:
- il devrait être en vie aussi longtemps que vous en avez besoin
- il doit être tué lorsque vous avez terminé avec lui
Le problème de la GC, c'est que si elle contribue à l'ancienne et, finalement, des garanties plus tard... cette "ultime" peut ne pas être suffisant. Si vous relâche un verrou, vous voulez vraiment qu'il soit libéré, de sorte qu'il ne bloque pas les appels supplémentaires!
Langues avec GC avez deux solutions:
- n'utilisez pas de GC, l'allocation de pile est suffisante: c'est normalement pour les problèmes de performances, mais dans notre cas, il aide vraiment car le champ d'application définit la durée de vie
-
using
construire... mais c'est explicite (faible) RAII alors qu'en C++ RAII est implicite, de sorte que l'utilisateur NE peut pas involontairement font l'erreur (en omettant l' using
mot-clé)
3. Pointeurs Intelligents
Pointeurs intelligents apparaissent souvent comme une balle d'argent à gérer la mémoire en C++
. Souvent de fois j'ai entendu: nous n'avons pas besoin GC après tout, puisque nous avons des pointeurs intelligents.
On ne pouvait pas être plus faux.
Pointeurs intelligents faire aider: auto_ptr
et unique_ptr
utilisation RAII concepts, très utile en effet. Ils sont si simples que vous pouvez écrire vous-même très facilement.
Lorsqu'un besoin de partager la propriété toutefois, il devient de plus en plus difficile: vous risquez de partager entre plusieurs threads et il ya quelques questions subtiles avec la manipulation de la comte. Donc, on va naturellement vers shared_ptr
.
C'est génial, c'est ce coup de pouce pour après tout, mais ce n'est pas une balle d'argent. En fait, le principal problème avec les shared_ptr
, c'est qu'il émule un GC mis en œuvre par Reference Counting
, mais vous avez besoin pour mettre en œuvre le cycle de détection de tous par vous-même... Urg
Bien sûr, il y a cette weak_ptr
truc, mais j'ai malheureusement déjà vu des fuites de mémoire en dépit de l'utilisation de shared_ptr
en raison de ces cycles... et quand vous êtes dans un environnement multithread, il est extrêmement difficile à détecter!
4. Quelle est la solution ?
Il n'y a pas de solution miracle, mais comme toujours, c'est certainement faisable. En l'absence de GC besoin d'être clair sur la propriété:
- préfèrent avoir un seul propriétaire à un moment donné, si possible
- si pas, assurez-vous que votre diagramme de classe n'a pas de cycle se rapportant à la propriété et à les briser avec de subtiles application de l'
weak_ptr
Donc en effet, il serait bon d'avoir un GC... mais c'est pas une question triviale. Et dans le même temps, nous avons juste besoin de se retrousser les manches.