Wie funktioniert der neue automatische Referenzzählmechanismus?

206

Kann mir jemand kurz erklären, wie ARC funktioniert? Ich weiß, dass es sich von Garbage Collection unterscheidet, aber ich habe mich nur gefragt, wie es genau funktioniert.

Wenn ARC das tut, was GC tut, ohne die Leistung zu beeinträchtigen, warum verwendet Java dann GC? Warum wird ARC nicht auch verwendet?

user635064
quelle
2
Hier erfahren Sie alles darüber: http://clang.llvm.org/docs/AutomaticReferenceCounting.html Wie es in Xcode und iOS 5 implementiert ist, steht unter NDA.
Morten Fast
14
@mbehan Das ist ein schlechter Rat. Ich möchte mich nicht anmelden oder sogar ein Konto für iOS Dev Center haben, aber ich bin trotzdem daran interessiert, etwas über ARC zu erfahren.
Andres F.
1
ARC macht nicht alles, was GC macht, es erfordert, dass Sie explizit mit starker und schwacher Referenzsemantik arbeiten, und es geht Speicher verloren, wenn Sie diese nicht richtig verstehen. Nach meiner Erfahrung ist dies zunächst schwierig, wenn Sie Blöcke in Objective-C verwenden, und selbst nachdem Sie die Tricks kennengelernt haben, bleibt Ihnen ein nerviger (IMO) Boilerplate-Code für viele Verwendungen von Blöcken übrig. Es ist bequemer, nur starke / schwache Referenzen zu vergessen. Darüber hinaus kann GC etwas besser abschneiden als ARC wrt. CPU, benötigt aber mehr Speicher. Es kann schneller sein als die explizite Speicherverwaltung, wenn Sie viel Speicher haben.
TaylanUB
@TaylanUB: "benötigt mehr Speicher". Viele Leute sagen das, aber ich finde es schwer zu glauben.
Jon Harrop
2
@ JonHarrop: Momentan erinnere ich mich nicht einmal daran, warum ich das gesagt habe, um ehrlich zu sein. :-) In der Zwischenzeit wurde mir klar, dass es so viele verschiedene GC-Strategien gibt, dass solche pauschalen Aussagen wahrscheinlich alle wertlos sind. Lassen Sie mich Hans Böhm aus seinen Mythen und Halbwahrheiten zur Gedächtnisallokation rezitieren : "Warum ist dieses Gebiet so anfällig für zweifelhafte Volksweisheiten?"
TaylanUB

Antworten:

244

Jeder neue Entwickler, der zu Objective-C kommt, muss die strengen Regeln für das Aufbewahren, Freigeben und automatische Freigeben von Objekten kennen. Diese Regeln legen sogar Namenskonventionen fest, die die Anzahl der von Methoden zurückgegebenen Objekte beibehalten. Die Speicherverwaltung in Objective-C wird zur zweiten Natur, wenn Sie sich diese Regeln zu Herzen nehmen und sie konsequent anwenden, aber selbst die erfahrensten Cocoa-Entwickler rutschen von Zeit zu Zeit aus.

Mit dem Clang Static Analyzer erkannten die LLVM-Entwickler, dass diese Regeln zuverlässig genug waren, um ein Tool zu erstellen, das auf Speicherverluste und Überfreigaben in den Pfaden Ihres Codes hinweist.

Die automatische Referenzzählung (ARC) ist der nächste logische Schritt. Wenn der Compiler erkennen kann, wo Sie Objekte aufbewahren und freigeben sollen, lassen Sie ihn diesen Code einfügen. Starre, sich wiederholende Aufgaben sind das, was Compiler und ihre Brüder großartig können. Menschen vergessen Dinge und machen Fehler, aber Computer sind viel konsistenter.

Dies befreit Sie jedoch nicht vollständig von der Sorge um die Speicherverwaltung auf diesen Plattformen. In meiner Antwort hier beschreibe ich das Hauptproblem, auf das Sie achten müssen (Zyklen beibehalten). Dies erfordert möglicherweise ein wenig Nachdenken von Ihrer Seite, um schwache Zeiger zu markieren. Dies ist jedoch im Vergleich zu dem, was Sie in ARC gewinnen, geringfügig.

Im Vergleich zur manuellen Speicherverwaltung und Speicherbereinigung bietet ARC das Beste aus beiden Welten, da keine Schreib- / Freigabecodes mehr geschrieben werden müssen, ohne dass die in einer Speicherbereinigungsumgebung angezeigten Speicherprofile zum Anhalten und Sägezahn vorhanden sind. Die einzigen Vorteile, die die Speicherbereinigung gegenüber dieser Funktion hat, sind ihre Fähigkeit, mit Aufbewahrungszyklen umzugehen, und die Tatsache, dass Zuweisungen atomarer Eigenschaften kostengünstig sind (wie hier erläutert ). Ich weiß, dass ich meinen gesamten vorhandenen Mac GC-Code durch ARC-Implementierungen ersetze.

Ob dies auf andere Sprachen ausgedehnt werden könnte, scheint auf das Referenzzählsystem in Objective-C ausgerichtet zu sein. Es mag schwierig sein, dies auf Java oder andere Sprachen anzuwenden, aber ich weiß nicht genug über die Details des Low-Level-Compilers, um dort eine endgültige Aussage zu treffen. Angesichts der Tatsache, dass Apple diese Bemühungen in LLVM vorantreibt, wird Objective-C an erster Stelle stehen, sofern nicht eine andere Partei selbst erhebliche Ressourcen dafür bereitstellt.

Die Enthüllung dieser schockierten Entwickler auf der WWDC, sodass die Leute nicht wussten, dass so etwas getan werden könnte. Es kann im Laufe der Zeit auf anderen Plattformen erscheinen, ist jedoch derzeit nur für LLVM und Objective-C verfügbar.

Brad Larson
quelle
56
Hervorhebung von mir: Dies befreit Sie nicht vollständig von der Sorge um die Speicherverwaltung
bshirley
6
Ist ARC wirklich eine Innovation? Aus Ihrer Antwort schließe ich, dass ARC ein neues Konzept ist, das zum ersten Mal in Objective-C verwendet wird (korrigieren Sie mich, wenn ich falsch liege). Um ehrlich zu sein, ich bin kein Objective-C-Entwickler und weiß nicht viel über ARC, aber sind Boost Shared Pointers (siehe boost.org) nicht genau dasselbe? Und wenn nicht, was ist der Unterschied?
theDmi
2
@DMM - Anstatt sich auf überladene Operatoren zu verlassen (wie es Boost tut), handelt es sich um einen Prozess auf Compilerebene, der sich über die gesamte Sprache erstreckt. Dies erleichtert unter anderem die Konvertierung einer manuell referenzgezählten Anwendung in ARC. Boost kann lokale Variablen auch anders behandeln als ARC, wobei ARC den Moment kennt, in dem eine lokale Variable nicht mehr verwendet wird und zu diesem Zeitpunkt freigegeben werden kann. Ich glaube, dass Sie mit Boost immer noch angeben müssen, dass Sie mit der Variablen fertig sind.
Brad Larson
6
Zur Beantwortung der Frage "Ist es neu?" Verfügt Delphi seit mehr als einem Jahrzehnt über eine automatische Referenzzählung für Zeichenfolgen, Arrays und Schnittstellen (für die COM-Unterstützung). Ich bin damit einverstanden, dass es wirklich ein guter Kompromiss zwischen einer gc-Umgebung und einer Umgebung ist, in der alles manuell erledigt wird. Ich bin froh, dass es in ObjC und LLVM ist (damit auch andere Sprachen davon profitieren können).
Davidmw
2
@theDmi: "Ist ARC wirklich eine Innovation?". Die automatische Referenzzählung wurde 1960 erfunden und in vielen Sprachen wie Python und Mathematica verwendet. Es wird nicht in der JVM oder CLR verwendet, da es sehr langsam ist und Zyklen leckt.
Jon Harrop
25

ARC spielt nur das alte Retain / Release (MRC) ab, wobei der Compiler herausfindet, wann Retain / Release aufgerufen werden soll. Es weist tendenziell eine höhere Leistung, eine geringere Spitzenauslastung des Speichers und eine besser vorhersehbare Leistung als ein GC-System auf.

Andererseits sind einige Arten von Datenstrukturen mit ARC (oder MRC) nicht möglich, während GC sie verarbeiten kann.

Wenn Sie beispielsweise eine Klasse mit dem Namen node haben und node ein NSArray mit untergeordneten Elementen und einen einzelnen Verweis auf das übergeordnete Element hat, der mit GC "nur funktioniert". Mit ARC (und auch der manuellen Referenzzählung) haben Sie ein Problem. Jeder gegebene Knoten wird von seinen untergeordneten und auch von seinen übergeordneten Knoten referenziert.

Mögen:

A -> [B1, B2, B3]
B1 -> A, B2 -> A, B3 -> A

Alles ist in Ordnung, während Sie A verwenden (z. B. über eine lokale Variable).

Wenn Sie damit fertig sind (und B1 / B2 / B3), wird ein GC-System schließlich entscheiden, alles zu betrachten, was es finden kann, beginnend mit den Stapel- und CPU-Registern. Es wird niemals A, B1, B2, B3 finden, also werden sie finalisiert und der Speicher in andere Objekte zurückgeführt.

Wenn Sie ARC oder MRC verwenden und mit A abschließen, hat es eine Nachzählung von 3 (B1, B2 und B3 beziehen sich alle darauf), und B1 / B2 / B3 haben alle eine Referenzanzahl von 1 (A's NSArray enthält eine Referenz auf jeder). Alle diese Objekte bleiben also lebendig, obwohl nichts sie jemals verwenden kann.

Die übliche Lösung besteht darin, zu entscheiden, dass eine dieser Referenzen schwach sein muss (nicht zur Referenzanzahl beitragen). Dies funktioniert für einige Verwendungsmuster, z. B. wenn Sie B1 / B2 / B3 nur über A referenzieren. In anderen Mustern schlägt dies jedoch fehl. Zum Beispiel, wenn Sie manchmal an B1 festhalten und erwarten, über den übergeordneten Zeiger wieder nach oben zu klettern und A zu finden. Mit einer schwachen Referenz, wenn Sie nur an B1 festhalten, kann (und wird) A verdampfen und B2 und B3 nehmen damit.

Manchmal ist dies kein Problem, aber einige sehr nützliche und natürliche Methoden zum Arbeiten mit komplexen Datenstrukturen sind mit ARC / MRC sehr schwierig zu verwenden.

ARC zielt also auf die gleichen Probleme ab, auf die GC abzielt. ARC arbeitet jedoch mit einer begrenzten Anzahl von Verwendungsmustern als GC. Wenn Sie also eine GC-Sprache (wie Java) verwenden und etwas wie ARC darauf pfropfen, funktionieren einige Programme nicht mehr (oder generieren zumindest Tonnen von verlassenem Speicher und kann schwerwiegende Auslagerungsprobleme verursachen oder nicht genügend Speicher oder Auslagerungsspeicher haben).

Sie können auch sagen, dass ARC der Leistung (oder vielleicht der Vorhersagbarkeit) eine höhere Priorität einräumt, während GC der generischen Lösung eine höhere Priorität einräumt. Infolgedessen hat GC weniger vorhersehbare CPU- / Speicheranforderungen und (normalerweise) eine geringere Leistung als ARC, kann jedoch jedes Nutzungsmuster verarbeiten. ARC funktioniert viel besser für viele, viele gängige Nutzungsmuster, aber für einige (gültige!) Nutzungsmuster fällt es um und stirbt ab.

Streifen
quelle
"Andererseits sind einige Arten von Datenstrukturen mit ARC nicht möglich." Ich denke, Sie meinten, eine automatische Bereinigung ist ohne Hinweise nicht möglich. offensichtlich sind die Datenstrukturen.
Steven Fisher
Sicher, aber NUR die automatische Bereinigung von ObjC-Objekten ist unter ARC verfügbar, also "keine automatische Bereinigung" == "keine Bereinigung". Ich werde umformulieren und dann antworten, wenn ich mehr Zeit habe.
Streifen
@Stripes: Das Äquivalent zur manuellen Bereinigung in ARC ist das manuelle Unterbrechen von Zyklen, z foo = nil.
Douglas
"[ARC] wird tendenziell eine höhere Leistung haben ... ARC legt größeren Wert auf Leistung". Ich bin überrascht zu lesen, dass das Zählen von Referenzen viel langsamer ist als das Verfolgen der Speicherbereinigung , wenn bekannt ist . fliegendefrogblog.blogspot.co.uk/2011/01/…
Jon Harrop
2
Theoretisch ist die GC schneller (jede Manipulation des Referenzzählers muss kohärent im Multiprozessor-Cache sein, und es gibt viele davon). In der Praxis ist das einzige verfügbare GC-System für ObjC viel langsamer. Es ist auch sehr häufig, dass GC-Systeme Threads zu zufälligen Zeiten für vom Benutzer wahrnehmbare Zeiträume anhalten (es gibt einige Echtzeit-GC-Systeme, aber sie sind nicht üblich, und ich denke, sie haben "interessante" Einschränkungen)
Stripes
4

Magie

Genauer gesagt funktioniert ARC genau so, wie Sie es mit Ihrem Code tun würden (mit bestimmten geringfügigen Unterschieden). ARC ist eine Technologie zur Kompilierungszeit, im Gegensatz zu GC, die zur Laufzeit verwendet wird und sich negativ auf Ihre Leistung auswirkt. ARC verfolgt die Verweise auf Objekte für Sie und synthetisiert die Retain / Release / Autorelease-Methoden gemäß den normalen Regeln. Aus diesem Grund kann ARC auch Dinge freigeben, sobald sie nicht mehr benötigt werden, anstatt sie nur aus Gründen der Konvention in einen Autorelease-Pool zu werfen.

Einige andere Verbesserungen umfassen das Nullsetzen schwacher Referenzen, das automatische Kopieren von Blöcken auf den Heap und allgemeine Beschleunigungen (6x für Autorelease-Pools!).

Eine ausführlichere Beschreibung der Funktionsweise finden Sie in den LLVM-Dokumenten zu ARC.

Joshua Weinberg
quelle
2
-1 "ARC ist eine Technologie zur Kompilierungszeit, im Gegensatz zu GC, die Laufzeit ist und sich negativ auf Ihre Leistung auswirkt." Referenzzählungen werden zur Laufzeit erhöht, was sehr ineffizient ist. Aus diesem Grund ist die Verfolgung von GCs wie JVM und .NET so viel schneller.
Jon Harrop
1
@ Jon: Hast du einen Beweis dafür? Nach meiner eigenen Lektüre scheinen neue RC-Algorithmen in der Regel genauso gut oder besser zu funktionieren als M & S GC.
Xryl669
1
@ xryl669: Eine vollständige Erklärung finden Sie im GC-Handbuch ( gchandbook.org ). Beachten Sie, dass Tracing! = M & S.
Jon Harrop
3

Es unterscheidet sich stark von der Speicherbereinigung. Haben Sie die Warnungen gesehen, die darauf hinweisen, dass möglicherweise Objekte in verschiedenen Leitungen auslaufen? Diese Anweisungen sagen Ihnen sogar, in welcher Zeile Sie das Objekt zugewiesen haben. Dies ist noch einen Schritt weiter gegangen und kann nun retain/ releaseAnweisungen fast 100% der Zeit an den richtigen Stellen einfügen , besser als die meisten Programmierer. Gelegentlich gibt es einige seltsame Fälle von beibehaltenen Objekten, bei denen Sie Hilfe benötigen.

FreeAsInBeer
quelle
0

Sehr gut erklärt durch Apple Entwickler Dokumentation. Lesen Sie "Wie ARC funktioniert"

Um sicherzustellen, dass Instanzen nicht verschwinden, solange sie noch benötigt werden, verfolgt ARC, wie viele Eigenschaften, Konstanten und Variablen derzeit auf jede Klasseninstanz verweisen. ARC gibt die Zuordnung einer Instanz nicht auf, solange noch mindestens ein aktiver Verweis auf diese Instanz vorhanden ist.

Um sicherzustellen, dass Instanzen nicht verschwinden, solange sie noch benötigt werden, verfolgt ARC, wie viele Eigenschaften, Konstanten und Variablen derzeit auf jede Klasseninstanz verweisen. ARC gibt die Zuordnung einer Instanz nicht auf, solange noch mindestens ein aktiver Verweis auf diese Instanz vorhanden ist.

Diff zu kennen. zwischen Garbage Collection und ARC: Lesen Sie dies

Lalit Kumar
quelle
0

ARC ist eine Compilerfunktion, die die automatische Speicherverwaltung von Objekten ermöglicht.

Anstatt sich merken zu müssen, wann und verwendet werden retain, releasesoll autorelease, wertet ARC die Lebensdaueranforderungen Ihrer Objekte aus und fügt beim Kompilieren automatisch die entsprechenden Speicherverwaltungsaufrufe für Sie ein. Der Compiler generiert auch geeignete Dealloc-Methoden für Sie.

Der Compiler fügt die erforderlichen retain/releaseAufrufe zur Kompilierungszeit ein, diese Aufrufe werden jedoch wie jeder andere Code zur Laufzeit ausgeführt.

Das folgende Diagramm gibt Ihnen ein besseres Verständnis der Funktionsweise von ARC.

Geben Sie hier die Bildbeschreibung ein

Diejenigen, die neu in der iOS-Entwicklung sind und keine Berufserfahrung mit Ziel C haben, finden Sie in der Apple-Dokumentation zum Programmierhandbuch für Advanced Memory Management ein besseres Verständnis der Speicherverwaltung.

Yogesh Bharate
quelle