RAII vs. Garbage Collector

Kürzlich sah ich einen tollen Vortrag von Herb Sutter über "Leck-Freien C++ - ..." bei CppCon 2016, wo er Sprach über die Verwendung von smart-Pointer implementieren RAII (Resource acquisition is initialization) - Konzepte und wie Sie zu lösen die meisten memory leaks Probleme.

Nun war ich gefragt. Wenn ich strikt RAII Regeln, das scheint eine gute Sache, warum sollte das anders sein von einem garbage collector in C++? Ich weiß, dass mit RAII ist der Programmierer die volle Kontrolle, wenn die Ressourcen werden freigegeben, wieder, aber, dass in jedem Fall von Vorteil nur mit einem garbage-collector? Wäre es wirklich weniger leistungsfähig? Ich habe sogar gehört, dass ein garbage collector effizienter sein kann, als es können Sie kostenlos mehr Speicher zu einer Zeit statt, die Befreiung der kleinen Speicher-Stücke, die alle über den code.

  • Deterministic resource management ist entscheidend, in allen möglichen Fällen, vor allem, wenn Sie den Umgang mit unmanaged Ressourcen (z.B. Datei-handles, Datenbanken, etc.). Außer, dass die garbage collection immer eine Art von overhead, in der Erwägung, dass RAII hat nicht mehr Aufwand als das schreiben den code korrekt in den ersten Platz. "Befreiung der kleinen Speicher-Stücke, die alle über den code" ist in der Regel Recht ein bisschen mehr effizient, weil es viel weniger störend für die Ausführung der Anwendung.
  • "warum sollte das anders sein von einem garbage collector in C++?" Ich erinnere mich Stroustrup sagt in einem seiner Vorträge (nicht wörtlich): "C++ ist Müll gesammelt, denn es hat RAII".
  • Während der "Befreiung der kleinen Speicher-Stücke, die alle über den code" könnte effizienter sein, die aus einer runtime-perfomance Sicht, es ist nicht von einer Speicher-Effizienz-Perspektive, wie es führt zu mehr Fragmentierung.
  • Es gibt viele Speicher-management-Strategien, die Fragmentierung zu verringern, ohne dass die garbage collection.
  • Stark Verwandte Frage: stackoverflow.com/questions/147130/...
  • Hinweis: Sie sprechen über Ressourcen, aber es gibt mehr als eine Art von Ressource. Ein garbage collector aufgerufen wird, wenn es Zeit ist, geben Sie Speicherplatz frei, aber es wird nicht aufgerufen, wenn es Zeit, schließen Sie die Dateien.
  • alles ist besser als garbage collection
  • RAII kann helfen, vermeiden Sie Leckagen, aber es nicht in sich selbst zu verhindern, verwenden-nach-frei, was ein GC in der Regel können.
  • RAII ist aufgrund seiner deterministischen Natur ermöglicht die Verwendung von Destruktoren, OTOH mit GC-es gibt keinen Punkt in es, da Sie nicht wissen, Wann es passiert
  • Herb Sutter ' s eigene deferred_ptr intelligente Zeiger verwendet, um bieten eine bequeme Schnittstelle, um die garbage collection. Es heißt deferred_ptr weil Destruktoren aufgeschoben werden, bis Sie Sie bitten, für eine Sammlung (so Sie noch vorkommen, in einer vorhersehbaren Zeit).
  • eine GC kann tatsächlich effizienter sein. Anstelle sich kostenlos die kleine von Speicher und auf der Suche nach dem kleinen bits zugeordnet werden kann, wieder (Fragmentierung des Speichers) eine GC können einfach kopieren Sie die wenigen live-Objekte und markieren Sie eine riesige Menge an Speicher so frei auf einmal. Da die meisten der Zeiten, die Programme machen viele kleine, kurzlebige Objekte, dies kann sehr nützlich sein.
  • Sie können immer noch Destruktoren mit einem GC. In der Tat, C# hat Sie. Aber Sie sind eigentlich nur eine Notlösung im Falle einer Ressource wurde nicht ordnungsgemäß geschlossen. Als Sie sagte, es ist nicht deterministisch, aber das macht es nicht sinnlos.
  • Java-Programme sicher zuordnen, viele kleine, kurzlebige Objekte (einfach weil es schwer ist tun viel von nichts in Java ohne Zuweisung), aber ich bin nicht sicher, dass C++ - Programme tun das gleiche, denn C++ ermöglicht die Zuordnung auf dem stack statt (einschließlich boost::container::small_vector und ähnliches). Ich wäre daran interessiert zu sehen eine Quelle, wenn Sie eine haben.
  • Wenn Sie voll sind verpflichtet, RAII und Verwendung der smart-Pointer überall, Sie sollten nicht use-after-free-Fehler. Aber auch wenn die GC (oder ref gezählt smart Pointer) würde gespeichert haben, werden Sie von der use-after-free-Fehler, könnte es sein, Maskierung ein Szenario, wo hast du unwissentlich gehalten Verweise auf Ressourcen länger, als Sie erwartet.
  • Ich nenne BS auf dieser wie ich schon zu jemand anderes, der sagte das gleiche, was ich entlarvt auf CS.SE. Wenn Sie ein legit Beispiel um das zu beweisen, zeigen Sie es. So weit wie ich kann sagen, es ist ein urbaner Mythos, der will einfach nicht sterben, weil die Menschen so verzweifelt, dass es um wahr zu sein.
  • Unterschiedliche Bedeutung von Effizienz. Ich habe ausdrücklich argumentiert, dass das RAII-Stil ist "weniger störenden Einfluss auf die Ausführung der Anwendung". Die Garbage collection hat, um das Programm zu stoppen die Ausführung, um zu tun, alle, die Kennzeichnung und die Befreiung auf einmal, was sehr störend. Nun, ich denke, man kann behaupten, dass die gleichzeitige garbage-Kollektoren existieren, aber Sie sind nicht sehr beliebt in der wildnis, und auch deutlich begrenzen den Durchsatz. Ich bin offen für eigentlich jemand zeigt einen benchmark, der zeigt, dass deterministische Zerstörung ist weniger effizient als die GC, aber ich habe noch keine gesehen. Ganz im Gegenteil. @patrick
  • Haben Sie nicht nur Probleme wie this in den Sprachen, die zur Umsetzung von RAII, ohne garbage collection. Natürlich gibt es workarounds, so gibt es für jedes problem, aber du gehst zu haben eine harte Zeit zu sagen, dass Kerl, der die garbage collection erhöht die Effizienz seiner app. Oder von seinem workflow, zu umgehen, indem man im Grunde nicht, Objekte zu erstellen. Und dieser Mythos über GC, der einzige Weg, um eine Zersplitterung zu vermeiden, muss sterben, auch. Schreiben Sie eine bessere Speicher-manager.
  • Nicht helfen, Kniebeugen mit nicht-besitzen-Referenzen.
  • Es ist ziemlich einfach zu zeigen, dass die GCs haben einen höheren Durchsatz als malloc/free; reserviert ein paar Dinge über den Haufen und werfen Sie Weg. Was schwerer ist, zeigt ein Fall, wo, was zählt. GC würd Sprachen neigen zu reservieren pathologisch große Mengen von Zeug, das ist, warum Sie benötigen einen GC, aber lower-level-Sprachen nicht, so dass der Durchsatz der Zuweisungen spielt keine Rolle, dass viel zu Ihnen.
  • Äh, Nein. Wenn Sie nur alloc/dealloc einen Haufen, dann alles, was Sie zu Messen ist, wie gut die heap-Implementierung ist im Umgang mit einem unrealistisch dumm Zuweisung Muster, nicht alles angibt, ist der eigentliche Unterschied zwischen Hand - & GC. Aber ich habe gerade versucht auf meinem computer (Windows) und MSVC nahm 1328 und 675 ms zu tun allocate und deallocate, VC# 2375 nahm und 0 ms, & GCC nahm 2421 und 2032 ms. Wenn wir könnten, ziehen Sie eine Schlussfolgerung aus dieser, Sie wäre immer noch falsch. Also, nochmals: zeigen Sie mir code (nicht Englisch), dass legitly (d.h. wissenschaftlich) zeigt die stärkste Forderung, die Sie machen können.
  • gist.github.com/Veedrac/a3f0d6a0b1a2a64a28ac6da4637ae59d
  • über use-after-free: ein GC-trades einen sofortigen Absturz in debug-Modus für eine automatische memory-leak-und phantom-Objekte. Ein symptom kann besser sein, als der andere, je nach Fall, aber immer noch der bug nicht gelöst wird.
  • Use-after-frees brechen, Ihren code, wenn Sie zerstören ein Objekt vor dem letzten Einsatz. Lecks verursachen transiente Ineffizienz, wenn Sie zerstören ein Objekt zu lange nach der letzten Verwendung. Diese disjunkt sind Probleme, und beheben nicht vorstellen das andere. Beachten Sie auch, dass RAII nicht wirklich beheben von Lecks, es ist nur räumt Sie ein wenig auf. Wenn ein GC ist das festhalten an einem Objekt zu lange, andere als nur die GC Sammlung Latenz, die äquivalente besitzen RAII-pointer wäre auch noch festzuhalten, dass-Objekt. Auch beachten Sie, dass use-after-frees in C++ nur selten verursachen reinigen stürzt ab, auch im debug.
  • Dass unklar war, mein schlechtes. Ich spreche über die Art von Fehlern, wo Ein Objekt verweist, und verwenden Sie ein Objekt B, wenn es sollte nicht mehr, denn B ist, soll tot sein. RAII (oder manuelle Verwaltung für diese Angelegenheit) töten B und hinterlassen Einen mit eindeutig ungültigen Zeiger, dass kann erkannt werden auf den nächsten Einsatz (entweder per segfault, oder mit tools wie valgrind). Eine GC wird halten B lebendig so lang wie Ein Leben. In den meisten Fällen das einzige symptom, abgesehen von dem leak, ist, dass Einer ruft veraltete Daten aus B, die möglicherweise wirklich schwer zu fassen.
  • so wählen Sie das symptom, das wird am einfachsten zu Debuggen macht Sinn, aber weder RAII noch ein GC beheben können, die zugrunde liegenden Fehler, der ein Programm Logik-Problem (korrekt meldende Ein, dass B tot sein sollte jetzt).
  • Das scheint wie eine wirklich seltsame Art der Betrachtung der Dinge. Etwas soll lebendig sein, wenn du gehst, es zu benutzen, nicht die andere Weise herum. Zwar gibt es auch Ausnahmen, ich würde postulieren, dass Sie sind eine kleine Minderheit.
  • Es ist wirklich nur eine andere Art zu formulieren, die Logik, die Sie implementieren müssen. "An diesem Punkt B muss sterben", "Periode" ist eine durchaus vernünftige design-Anforderung. Es kommt die ganze Zeit in der Spiele-Entwicklung: zum Beispiel B ist ein Feind, du hast gerade getötet, und a ist Eine zielsuchende Rakete noch im Flug in Richtung B. Lassen Sie uns sagen, dass Sie vergessen, Benachrichtigen Sie Einen, das ist ein bug. Wenn Sie gewaltsam töten B, das Spiel stürzt auf dem nächsten Bild. Wenn B ist, lebendig gehalten durch eine GC, die Rakete wird jetzt orbit um eine unsichtbare, immaterielle Feind, denn niemand anderes ist, als Eine weiß, dass es tatsächlich noch am Leben.
  • Ein (vernünftiges) Spiel nicht das umsetzen dieser Logik mit Destruktoren, weil alles in arrays verweisen auf andere arrays. Zu versuchen, "gewaltsam töten", B durch die memory management einfach nicht skalieren, und auch wenn es möglich war, Sie würde sicherlich nicht geben Ihnen einen sauberen Absturz. Sie haben zu handhaben, das Zeug, wenn man die Iteration durch das array von Raketen, und das bedeutet, dass Sie benötigen, um irgendeine Art getötet Flagge auf B, die, wenn Sie nichts bedeutet B muss noch gültig sein Gedächtnis!
  • Das ist nur ein Beispiel, wenn eine unmittelbare Objekt der Tod erforderlich ist. Es gibt viele Möglichkeiten, um dies zu implementieren das Verhalten und die direkte Benachrichtigung könnte tatsächlich interessant sein, in einigen Fällen (live-Fahnen und zwei-pass-Zerstörung sind eine weitere beliebte Umsetzung in der Tat). Aber mein Punkt ist: wenn Sie ein Objekt verwenden, nachdem seine vorgesehene Lebensdauer, dann ist entweder die, die Sie geschrieben haben einen Fehler(vergessen, Benachrichtigen Sie Einen), oder du unterschätzt die Lebensdauer-Anforderungen bei der Gestaltung (warten, B Leben müssen, eine weitere Rahmen für alle zu Zeugen dessen Tod). Ein GC nicht lösen, es wird ändern Sie einfach das symptom.
  • Oder, am wahrscheinlichsten, die Sie gerade zerstört es noch zu früh. Trivial einfach zu tun, in C++.
  • Schwache Zeiger helfen Kniebeuge mit nicht-besitzen-Referenzen.
  • Es gibt eine ganze Reihe von Problemen mit der Verwendung von weak-Pointer auf diese Weise. Es ist nicht nur die Gottlosen langsam, aber es ist semantisch völlig falsch. Schwache Zeiger für den Fall bieten, wo die referenzierten Daten verschwindet, machen Sie den Fall behandeln, wo Sie erhalten eine plötzliche use-after-free, aber Sie don ' T stop the use-after-free passiert. (Selbst wenn Sie es täten, C++, verwendet nicht-besitzenden Referenzen viel zu liberal für die, die helfen.) Zu beheben, die in einer RAII-system müssen Sie so etwas wie Rust ' s borrow-checker, aber das kommt mit seinen eigenen Nachteile.
  • OK, das ist besser (Java ist 10x "schneller"). Aber nochmal: es ist immer noch schrecklich. Wieder die gleiche Frage: du bist benchmarking der heap-Implementierung, nicht GC vs. manuelle Speicherverwaltung. Zum Beispiel habe ich versucht, läuft Ihr code mit einem einfachen Haufen, die recycelt Knoten anstelle von de-Zuweisung und Neuzuweisung (nur stellen Sie Vektor-und überlast -Tree::operator {new,delete}), und Java ging von 10x schneller zu 3x schneller. Ich werde nicht Zeit verschwenden auf den rest der Flaschenhals, aber es ist eindeutig in der Zuweisung Funktionen, die ich optimieren können auch. (Fortsetzung)
  • Und bevor Sie springen auf mich und sagen, ich habe geschummelt, beachten Sie, dass Java nicht selbst freigeben, überhaupt nichts, also dein Vergleich ist schon unfair, und ich bin nur so dass es noch fair ist. (Ich sage es System.gc() bevor die timing-Messungen, aber ich sehe nicht den Speicher an das system zurückgegeben an alle.) Das heißt, auch wenn es zurück die Erinnerung an diesem Punkt, würde dies nur beweisen, Ihre heap-Implementierung ist gut abgestimmt für den Fall, wo die GC nicht selbst ausführen zu müssen. Es kaum so etwas sagt, wie z.B. der Möglichkeit, einen GC zu verbinden deallocations macht es schneller als die manuelle deallocations.
  • Sie streiten gegen einen Punkt, den ich nicht machen. Da ist nichts unfair, der Vergleich, die ich gemacht, solange Sie nicht davon ausgehen, dass ich etwas gesagt, wusste ich nicht.
  • Natürlich ist es unfair. Sie gab mir zwei Programme zu vergleichen, eine, die Speicher freigibt, eine, die nicht. Um einen fairen Vergleich, den Sie ausführen müssen, um eine realistische Arbeitsbelastung, die tatsächlich erfordert die GC, wissen Sie, kick in. Sitzt nicht im Leerlauf. Und Sie brauchen, um eine dynamische und realistische memory allocation Muster, nicht-FIFO-oder LIFO-oder eine Variante davon. Es ist nicht gerade unfassbar zu behaupten, dass ein Programm, das nie freigibt Speicher ist schneller als eine, die funktioniert, oder dass ein Haufen dran LIFO deallocations wird schneller sein als eine, die nicht. Nun, duh, natürlich, es wäre.
  • Ihre Kritik keinen Sinn machen. Läuft System.gc(); System.runFinalization(); dauert fast keine Zeit (offensichtlich, wenn Sie verstehen, wie die GCs Arbeit) und befreit alle junk "Zuweisung". GCs sind auch besser bei der Speicherfreigabe schnell, als Sie auf die Zuteilung von Speicher schnell. Eine "realistische", aber ebenso schwere Allokation-Muster ist nur noch zu verschärfen dieses Problem (auch offensichtlich, wenn Sie verstehen, wie GCs), da verdichten GCs Griff Fragmentierung viel besser als malloc/free, und Ihre tiered deallocations sind genau für solche workloads.
  • Du wolltest Beweise für meine Behauptung, und ich zeigte Ihnen Beweise dafür. Sie sind unnötig defensive von Ihren Vorurteilen, anstatt einfach zu akzeptieren diesen Punkt und bewegen Sie sich auf etwas produktiver, wie Sie wissen, ein argument, das zählt.
  • nur durch die Annahme von" was ist der Punkt? Der Punkt, dass ein Programm mit einem GC, der nie selbst läuft (was dasselbe ist, ein Programm mit deaktiviertem GC) wird schneller sein als eine, die manuell befreit seine Erinnerung? Ja, es ist. Ein system, das nicht einmal seine Arbeit zu tun, wird schneller sein als eine, die das macht. Definitiv ein sehr gültiges argument, und Sie völlig blies meinen Verstand Recht gibt, indem Sie nur, wie elegant Sie erwies GCs kann schneller sein als die manuelle Speicherverwaltung. Sehr wissenschaftlich, Brillant, realistisch, und alles rund um gut getan, in der Tat.
  • Wieder, Sie sind nicht uneins mit dem Punkt, den ich gemacht habe. I war nicht die beabsichtigen, auf weht Ihre Meinung, ich habe nur versucht zu rechtfertigen ist eine ziemlich offensichtliche, unbestrittene Behauptung, dass im Prinzip alle damit einverstanden sind. Warum ist das ein problem?
  • ist das ein problem"? Die Diskussion über GC vs. manuelles memory-management, zeig mir ein Programm, wo die GC nicht noch Speicher frei, und Sie Fragen mich "warum ist das ein problem"? Gut, wenn das ist Ihr Spiel, dann halt die Speicherfreigabe in der manuellen version auch, und dann vergleichen. Warum ist das ein problem?!
  • Ich zeigte Ihnen genau, wie stellen Sie sicher, eine GC-routine wird ausgeführt, bevor das Programm endet. (Offensichtlich), macht praktisch null Unterschied zu den Ergebnissen.
  • Und ich habe bereits gesagt Sie genau, dass die GC verweigert, um Speicher frei, obwohl ich es lief. Sie wissen einfach nicht, Lesen anscheinend. Aber die Hölle, selbst wenn es das hat es ausgelöst, es wäre befreien Sie alle der Speicher reserviert in 1 gedreht, d.h. die Objekte wold alle sterben werden, in der ersten generation, d.h. der garbage collection ist künstlich auf 100% Wirkungsgrad, also eine völlig unrealistische Szenario, außer vielleicht in Ihrer Welt. Ja, du hast völlig überzeugend.
  • Nur, dass Sie nicht verstehen, wie Erinnerung funktioniert. Allocators nicht wieder den Arbeitsspeicher des Systems zu, Sie schicken Sie es zu Ihrer gratis-pools. malloc/free normal arbeiten genau das gleiche. Sie sollten mit Runtime runtime = Runtime.getRuntime(); System.out.println("Used Memory:" + (runtime.totalMemory() - runtime.freeMemory())); oder somesuch zum Messen der tatsächlichen Speicher zu verwenden.
  • Nein, es ist eher so, dass Sie nicht verstehen, wie Erinnerung funktioniert. malloc/free definitiv NICHT funktionieren genau das gleiche. Überprüfen Sie Ihre task-manager. Sie machen einen Hölle, sehr viel mehr Arbeit, als nur den Arbeitsspeicher, um das Programm; Sie sind der Rückgabe Sie den freigegebenen Speicher für den ganzen rest des Betriebssystems. Wenn Sie ein äpfel mit äpfeln Vergleich, dass, wie ich oben über den Knoten recycling, erhalten Sie anteilig bessere performance, wie ich schon oben gesagt. Wenn äpfel mit Birnen vergleichen ist, Sie Ihren Weg aber dann weiß ich nicht, was Sie zu sagen.
  • Ich bin hier fertig.
  • Ich sehe nicht, wie man vermute, dass ein use-after-free-bug ist "wahrscheinlich" ein Objekt, das früh starb und nicht eine Referenz gehalten zu lange, da beide Klassen von Fehlern existieren. Aber ich habe meine Stelle, damit ich nicht Durcheinander dieser Kommentar-thread weiter.
  • Meine Standard-Annahme ist, dass wenn ich einen Zeiger, es ist, weil ich will, was, die Zeiger, nicht so, dass ich einen Zeiger, weil ich es habe.

InformationsquelleAutor Jiddoo | 2017-06-02
Schreibe einen Kommentar