Wie ich sehen kann, werden intelligente Zeiger in vielen realen C ++ - Projekten häufig verwendet.
Obwohl eine Art von intelligenten Zeigern offensichtlich für die Unterstützung von RAII- und Eigentumsübertragungen von Vorteil ist, gibt es auch einen Trend, standardmäßig gemeinsame Zeiger als " Speicherbereinigungsmethode " zu verwenden , damit der Programmierer nicht so viel über die Zuordnung nachdenken muss .
Warum sind geteilte Zeiger beliebter als die Integration eines richtigen Müllsammlers wie Boehm GC ? (Oder stimmen Sie überhaupt zu, dass sie beliebter sind als die eigentlichen GCs?)
Ich kenne zwei Vorteile herkömmlicher GCs gegenüber der Referenzzählung:
- Herkömmliche GC-Algorithmen haben kein Problem mit Referenzzyklen .
- Die Referenzzählung ist im Allgemeinen langsamer als eine richtige GC.
Was sind die Gründe für die Verwendung von intelligenten Zeigern mit Referenzzählung?
quelle
std::unique_ptr
ist dies ausreichend und daher ist der Overhead für Raw-Zeiger in Bezug auf die Laufzeitleistung gleich Null. Durch die Verwendung von "std::shared_ptr
Überall" wird auch die Besitzersemantik verdeckt, und einer der Hauptvorteile von intelligenten Zeigern als die automatische Ressourcenverwaltung entfällt.Antworten:
Einige Vorteile der Referenzzählung gegenüber der Speicherbereinigung:
Geringer Overhead. Garbage Collectors können sehr aufdringlich sein (z. B. das Einfrieren Ihres Programms zu unvorhersehbaren Zeiten, während ein Garbage Collection-Zyklus abläuft) und sehr speicherintensiv (z. B. wächst der Speicherbedarf Ihres Prozesses unnötigerweise auf viele Megabyte, bevor die Garbage Collection endlich einsetzt).
Vorhersehbares Verhalten. Mit der Referenzzählung können Sie sicher sein, dass Ihr Objekt sofort freigegeben wird, wenn die letzte Referenz darauf nicht mehr vorhanden ist. Bei der Garbage Collection hingegen wird Ihr Objekt "irgendwann" freigegeben, wenn das System sich darum kümmert. Für RAM ist dies normalerweise kein großes Problem auf Desktops oder Servern mit geringer Auslastung. Für andere Ressourcen (z. B. Dateihandles) müssen diese jedoch häufig so schnell wie möglich geschlossen werden, um mögliche spätere Konflikte zu vermeiden.
Einfacher. Die Referenzzählung kann in wenigen Minuten erklärt und in ein oder zwei Stunden implementiert werden. Abfallsammler, insbesondere solche mit anständiger Leistung, sind äußerst komplex und werden von wenigen verstanden.
Standard. C ++ enthält Referenzzählung (über shared_ptr) und Freunde in der STL, was bedeutet, dass die meisten C ++ - Programmierer damit vertraut sind und der meiste C ++ - Code damit arbeiten wird. Es gibt jedoch keinen Standard-C ++ - Garbage-Collector, was bedeutet, dass Sie einen auswählen müssen und hoffen, dass er für Ihren Anwendungsfall gut funktioniert - und wenn dies nicht der Fall ist, müssen Sie das Problem beheben, nicht die Sprache.
Was die angeblichen Nachteile des Referenzzählens angeht - es ist ein Problem, Zyklen nicht zu erkennen, das ich in den letzten zehn Jahren bei der Verwendung des Referenzzählens noch nie persönlich erlebt habe. Die meisten Datenstrukturen sind von Natur aus azyklisch, und wenn Sie auf eine Situation stoßen, in der Sie zyklische Referenzen benötigen (z. B. übergeordneter Zeiger in einem Baumknoten), können Sie einfach einen weak_ptr- oder einen rohen C-Zeiger für die "Rückwärtsrichtung" verwenden. Solange Sie das potenzielle Problem beim Entwerfen Ihrer Datenstrukturen kennen, ist dies kein Problem.
Was die Leistung betrifft, hatte ich nie ein Problem mit der Leistung der Referenzzählung. Ich hatte Probleme mit der Leistung der Garbage Collection, insbesondere mit den zufälligen Einfrierungen, die GC verursachen kann. Die einzige Lösung ("Objekte nicht zuweisen") könnte genauso gut umformuliert werden wie "GC nicht verwenden". .
quelle
make_shared
bei der Rückgabe vorgehen . Dennoch ist die Latenz in Echtzeitanwendungen tendenziell das größere Problem, aber der Durchsatz ist im Allgemeinen wichtiger, weshalb Trace-GCs so häufig eingesetzt werden. Ich würde nicht so schnell schlecht über sie sprechen.Um eine gute Leistung eines GC zu erzielen, muss der GC in der Lage sein, Objekte im Speicher zu verschieben. In einer Sprache wie C ++, in der Sie direkt mit Speicherorten interagieren können, ist dies so gut wie unmöglich. (Microsoft C ++ / CLR zählt nicht, da es eine neue Syntax für GC-verwaltete Zeiger einführt und somit effektiv eine andere Sprache ist.)
Der Boehm-GC ist zwar eine clevere Idee, aber tatsächlich das Schlimmste aus beiden Welten: Sie benötigen ein malloc (), das langsamer als ein guter GC ist, und verlieren daher das deterministische Zuordnungs- / Freigabeverhalten ohne den entsprechenden Leistungsschub eines Generations-GC . Außerdem ist es zwangsläufig konservativ, sodass nicht unbedingt der gesamte Müll eingesammelt wird.
Ein guter, gut abgestimmter GC kann eine großartige Sache sein. Aber in einer Sprache wie C ++ sind die Gewinne minimal und die Kosten sind es oft nicht wert.
Mit zunehmender Beliebtheit von C ++ 11 wird es jedoch interessant sein zu sehen, ob Lambdas und Capture-Semantik die C ++ - Community zu denselben Zuordnungs- und Objektlebensdauerproblemen führen, die die Lisp-Community veranlasste, GCs zu erfinden Ort.
Siehe auch meine Antwort auf eine verwandte Frage über StackOverflow .
quelle
Richtig, aber objektiv gesehen ist die überwiegende Mehrheit des Codes jetzt in modernen Sprachen geschrieben, wobei die Garbage Collectors nachverfolgt werden.
Das ist eine schlechte Idee, weil Sie sich immer noch um die Zyklen kümmern müssen.
Oh wow, es gibt so viele Dinge, die an deiner Denkweise falsch sind:
Böhms GC ist im wahrsten Sinne des Wortes kein "richtiger" GC. Es ist wirklich schrecklich. Es ist konservativ, so dass es ausläuft und konstruktionsbedingt ineffizient ist. Siehe: http://flyingfrogblog.blogspot.co.uk/search/label/boehm
Geteilte Zeiger sind objektiv gesehen bei weitem nicht so beliebt wie GC, da die überwiegende Mehrheit der Entwickler jetzt GC-basierte Sprachen verwendet und keine gemeinsamen Zeiger benötigt. Schauen Sie sich Java und Javascript auf dem Arbeitsmarkt im Vergleich zu C ++ an.
Sie scheinen die Betrachtung auf C ++ zu beschränken, da Sie, wie ich annehme, glauben, dass GC ein tangentiales Problem ist. Dies ist nicht der Fall (der einzige Weg, um einen anständigen GC zu erhalten, besteht darin, die Sprache und die VM von Anfang an dafür zu entwerfen). Sie führen also eine Auswahlverzerrung ein. Leute, die wirklich eine ordnungsgemäße Garbage Collection wollen, bleiben nicht bei C ++.
Sie sind auf C ++ beschränkt, wünschen sich jedoch eine automatische Speicherverwaltung.
quelle
In MacOS X und iOS und bei Entwicklern, die Objective-C oder Swift verwenden, ist die Referenzzählung beliebt, da sie automatisch verarbeitet wird und die Verwendung der Müllabfuhr erheblich reduziert wurde, da Apple sie nicht mehr unterstützt (es wird mir mitgeteilt, dass Apps verwendet werden) Die Garbage Collection wird in der nächsten MacOS X-Version nicht mehr funktionieren, und die Garbage Collection wurde in iOS nie implementiert. Ich bezweifle wirklich ernsthaft, dass es jemals viel Software gab, die Garbage Collection verwendete, als es verfügbar war.
Der Grund für die Beseitigung der Garbage Collection: In einer Umgebung im C-Stil, in der Zeiger in Bereiche "entweichen" könnten, auf die der Garbage Collector keinen Zugriff hat, funktionierte dies nie zuverlässig. Apple ist der festen Überzeugung, dass die Referenzzählung schneller ist. (Sie können hier keine Angaben zur relativen Geschwindigkeit machen, aber niemand hat Apple überzeugen können). Und am Ende nutzte niemand die Müllabfuhr.
Das erste, was jeder MacOS X- oder iOS-Entwickler lernt, ist der Umgang mit Referenzzyklen. Für einen echten Entwickler ist das also kein Problem.
quelle
Der größte Nachteil der Garbage Collection in C ++ ist, dass es unmöglich ist, richtig zu machen:
In C ++ leben Zeiger nicht in einer eigenen Walled Community, sondern werden mit anderen Daten gemischt. Als solches können Sie einen Zeiger nicht von anderen Daten unterscheiden, die zufällig ein Bitmuster haben, das als gültiger Zeiger interpretiert werden kann.
Konsequenz: In jedem C ++ - Garbage Collector werden Objekte gelöscht, die gesammelt werden sollten.
In C ++ können Sie Zeigerarithmetik ausführen, um Zeiger abzuleiten. Wenn Sie also keinen Zeiger auf den Anfang eines Blocks finden, bedeutet dies nicht, dass auf diesen Block nicht verwiesen werden kann.
Konsequenz: Jeder C ++ - Garbage Collector muss diese Anpassungen berücksichtigen und jede Bitsequenz, die auf eine beliebige Stelle innerhalb eines Blocks zeigt, einschließlich unmittelbar danach, als gültigen Zeiger behandeln, der auf den Block verweist.
Hinweis: Kein C ++ - Garbage Collector kann Code mit folgenden Tricks verarbeiten:
Richtig, dies ruft undefiniertes Verhalten hervor. Aber ein vorhandener Code ist schlauer als gut für ihn und kann eine vorläufige Freigabe durch einen Garbage Collector auslösen.
quelle