Come funziona il nuovo meccanismo di conteggio automatico dei riferimenti?


206

Qualcuno può spiegarmi brevemente come funziona ARC? So che è diverso dalla Garbage Collection, ma mi chiedevo esattamente come funzionasse.

Inoltre, se ARC fa ciò che fa GC senza ostacolare le prestazioni, perché Java usa GC? Perché non usa anche ARC?


2
Questo ti dirà tutto: http://clang.llvm.org/docs/AutomaticReferenceCounting.html Come è implementato in Xcode e iOS 5 sotto NDA.
Morten Fast,

14
@mbehan È un consiglio scadente. Non voglio accedere o avere un account per iOS Dev Center, ma sono comunque interessato a conoscere ARC.
Andres F.

1
ARC non fa tutto ciò che fa GC, richiede di lavorare esplicitamente con la semantica di riferimento forte e debole e perde la memoria se non si ottengono quelli giusti. Nella mia esperienza, questo è inizialmente difficile quando usi i blocchi in Objective-C, e anche dopo aver appreso dei trucchi ti rimane un codice di caldaia (IMO) fastidioso attorno a molti usi dei blocchi. È più conveniente dimenticare solo riferimenti forti / deboli. Inoltre, GC può ottenere prestazioni leggermente migliori rispetto a ARC wrt. CPU, ma richiede più memoria. Può essere più veloce della gestione esplicita della memoria quando si ha molta memoria.
TaylanUB,

@TaylanUB: "richiede più memoria". Molte persone lo dicono, ma trovo difficile crederci.
Jon Harrop,

2
@JonHarrop: al momento non ricordo nemmeno perché l'ho detto, a dire il vero. :-) Nel frattempo mi sono reso conto che ci sono così tante diverse strategie GC che tali affermazioni generali sono probabilmente tutte senza valore. Permettetemi di recitare Hans Boehm dai suoi miti e mezze verità di allocazione della memoria : "Perché quest'area è così soggetta a sagge saggezza popolari?"
TaylanUB,

Risposte:


244

Ogni nuovo sviluppatore che arriva a Objective-C deve imparare le rigide regole su quando conservare, rilasciare e rilasciare automaticamente gli oggetti. Queste regole specificano anche le convenzioni di denominazione che implicano il conteggio degli oggetti restituiti dai metodi. La gestione della memoria in Objective-C diventa una seconda natura quando si prendono a cuore queste regole e le si applicano in modo coerente, ma anche gli sviluppatori Cocoa più esperti saltano di tanto in tanto.

Con Clang Static Analyzer, gli sviluppatori di LLVM hanno capito che queste regole erano abbastanza affidabili da poter costruire uno strumento per segnalare perdite di memoria e rilasci in eccesso all'interno dei percorsi che il tuo codice prende.

Il conteggio dei riferimenti automatici (ARC) è il passo logico successivo. Se il compilatore è in grado di riconoscere dove si dovrebbero conservare e rilasciare oggetti, perché non è necessario inserire quel codice? Compiti rigidi e ripetitivi sono ciò che i compilatori e i loro fratelli sono bravi a fare. Gli umani dimenticano le cose e commettono errori, ma i computer sono molto più coerenti.

Tuttavia, questo non ti libera completamente dal preoccuparti della gestione della memoria su queste piattaforme. Descrivo il problema principale a cui prestare attenzione (mantenere i cicli) nella mia risposta qui , che potrebbe richiedere un piccolo pensiero da parte vostra per contrassegnare i punti deboli. Tuttavia, questo è minore rispetto a quello che stai guadagnando in ARC.

Rispetto alla gestione manuale della memoria e alla garbage collection, ARC offre il meglio di entrambi i mondi eliminando la necessità di scrivere codice di conservazione / rilascio, senza avere i profili di memoria di arresto e dente di sega visti in un ambiente garbage collection. Gli unici vantaggi che la raccolta dei rifiuti ha su questo sono la sua capacità di gestire i cicli di mantenimento e il fatto che le assegnazioni di proprietà atomiche sono economiche (come discusso qui ). So che sto sostituendo tutto il mio codice GC GC esistente con implementazioni ARC.

Se questo possa essere esteso ad altre lingue, sembra orientato al sistema di conteggio dei riferimenti in Objective-C. Potrebbe essere difficile applicarlo a Java o ad altre lingue, ma non conosco abbastanza i dettagli del compilatore di basso livello per fare una dichiarazione definitiva lì. Dato che Apple è quella che sta spingendo questo sforzo in LLVM, Objective-C verrà prima a meno che un'altra parte non impegna risorse significative proprie a questo.

La presentazione di questi sviluppatori scioccati al WWDC, quindi le persone non erano consapevoli che si potesse fare qualcosa del genere. Potrebbe apparire su altre piattaforme nel tempo, ma per ora è esclusivo di LLVM e Objective-C.


56
enfasi mia: questo non ti libera completamente dal preoccuparti della gestione della memoria
bshirley,

6
ARC è davvero un'innovazione? Dalla tua risposta concludo che ARC è un nuovo concetto, che viene utilizzato per la prima volta in Objective-C (correggimi se sbaglio). Ad essere sincero, non sono uno sviluppatore di Objective-C e non conosco molto bene ARC, ma Boost Shared Pointers (vedi boost.org) non è esattamente la stessa cosa? E se non lo sono, qual è la differenza?
theDmi

2
@DMM - Invece di fare affidamento su operatori sovraccarichi (come fa Boost), questo è un processo a livello di compilatore, che lo estende su tutto il linguaggio. Tra le altre cose, ciò semplifica la conversione in ARC di un'applicazione conteggiata con riferimento manuale. Boost potrebbe anche gestire le variabili locali in modo diverso rispetto a ARC, dove ARC conosce il momento in cui una variabile locale non viene più utilizzata e può rilasciare a quel punto. Credo che con Boost devi ancora specificare in qualche modo che hai finito con la variabile.
Brad Larson

6
Per rispondere alla domanda "è nuova", Delphi ha contato automaticamente i riferimenti per stringhe, array e interfacce (per il supporto COM) da oltre un decennio. Sono d'accordo che sia davvero un bel compromesso tra un ambiente gc'd e un ambiente "fai tutto manualmente". Sono contento che sia in ObjC e LLVM (quindi anche altre lingue possono trarne vantaggio).
davidmw,

2
@theDmi: "ARC è davvero un'innovazione?". Il conteggio automatico dei riferimenti è stato inventato nel 1960 ed è stato utilizzato in molte lingue come Python e Mathematica. Non viene utilizzato in JVM o CLR perché è molto lento e perde cicli.
Jon Harrop,

25

ARC è solo il vecchio Mantieni / Rilascio (MRC) con il compilatore che capisce quando chiamare il blocco / rilascio. Tenderà ad avere prestazioni più elevate, un utilizzo di memoria di picco inferiore e prestazioni più prevedibili rispetto a un sistema GC.

D'altra parte alcuni tipi di strutture dati non sono possibili con ARC (o MRC), mentre GC può gestirli.

Ad esempio, se si dispone di una classe denominata nodo e il nodo ha un NSArray di figli e un singolo riferimento al suo genitore che "funziona" con GC. Con ARC (e anche il conteggio dei riferimenti manuali) hai un problema. Ogni nodo dato sarà referenziato dai suoi figli e anche dai suoi genitori.

Piace:

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

Tutto va bene mentre stai usando A (ad esempio tramite una variabile locale).

Quando hai finito (e B1 / B2 / B3), un sistema GC alla fine deciderà di guardare tutto ciò che può trovare a partire dallo stack e dai registri della CPU. Non troverà mai A, B1, B2, B3, quindi li finalizzerà e riciclerà la memoria in altri oggetti.

Quando si utilizza ARC o MRC e si termina con A, si ha un refcount di 3 (B1, B2 e B3 fanno tutti riferimento a esso) e B1 / B2 / B3 avrà tutti un conteggio di riferimento di 1 (l'array NS di A contiene un riferimento a ogni). Quindi tutti quegli oggetti rimangono vivi anche se nulla può mai usarli.

La soluzione comune è decidere che uno di quei riferimenti deve essere debole (non contribuire al conteggio dei riferimenti). Ciò funzionerà per alcuni schemi di utilizzo, ad esempio se si fa riferimento a B1 / B2 / B3 solo tramite A. Tuttavia, in altri schemi fallisce. Ad esempio, se a volte tieni premuto B1 e ti aspetti di risalire tramite il puntatore genitore e trovare A. Con un riferimento debole se tieni premuto B1, A può (e normalmente) evaporare e prendere B2 e B3 con esso.

A volte questo non è un problema, ma alcuni modi molto utili e naturali di lavorare con strutture complesse di dati sono molto difficili da usare con ARC / MRC.

Quindi ARC affronta lo stesso tipo di problemi target GC. Tuttavia ARC funziona su un set più limitato di schemi di utilizzo rispetto a GC, quindi se prendessi un linguaggio GC (come Java) e innesti qualcosa come ARC su di esso alcuni programmi non funzionerebbero più (o almeno genererebbero tonnellate di memoria abbandonata e può causare seri problemi di scambio o esaurire la memoria o lo spazio di scambio).

Puoi anche dire che ARC attribuisce una priorità maggiore alle prestazioni (o forse alla prevedibilità) mentre GC attribuisce una priorità maggiore all'essere una soluzione generica. Di conseguenza, GC ha requisiti CPU / memoria meno prevedibili e prestazioni (normalmente) inferiori rispetto ad ARC, ma può gestire qualsiasi modello di utilizzo. ARC funzionerà molto meglio per molti modelli di utilizzo comuni, ma per alcuni (validi!) Modelli di utilizzo cadrà e morirà.


"D'altra parte alcuni tipi di struttura dei dati non sono possibili con ARC" Penso che tu abbia inteso che la pulizia automatica non è possibile senza suggerimenti; ovviamente, le strutture dati sono.
Steven Fisher,

Certo, ma SOLO la pulizia automatica degli oggetti ObjC è disponibile sotto ARC, quindi "nessuna pulizia automatica" == "nessuna pulizia". Riscriverò quindi risponderò quando avrò più tempo.
Stripes

@Stripes: l'equivalente della pulizia manuale in ARC è l'interruzione manuale dei cicli, ad es foo = nil.
Douglas,

"[ARC] tenderà ad avere prestazioni più elevate ... ARC attribuisce una priorità maggiore alle prestazioni". Sono sorpreso di leggere che, quando è noto, il conteggio dei riferimenti è molto più lento della traccia della raccolta dei rifiuti. flyingfrogblog.blogspot.co.uk/2011/01/…
Jon Harrop,

2
In teoria GC è più veloce (ogni manipolazione del conteggio dei riferimenti deve essere coerente con la cache multiprocessore, e ce ne sono molti). In pratica, l'unico sistema GC disponibile per ObjC è molto più lento. È anche estremamente comune per i sistemi GC mettere in pausa i thread in momenti casuali per periodi di tempo percepibili dall'utente (ci sono alcuni sistemi GC in tempo reale, ma non sono comuni e penso che abbiano vincoli "interessanti")
Stripes

4

Magia

Ma più specificamente ARC funziona facendo esattamente quello che faresti con il tuo codice (con alcune differenze minori). ARC è una tecnologia di compilazione, a differenza di GC che è runtime e influirà negativamente sulle prestazioni. ARC monitorerà i riferimenti agli oggetti per te e sintetizzerà i metodi di mantenimento / rilascio / rilascio automatico secondo le normali regole. Per questo motivo ARC può anche rilasciare le cose non appena non sono più necessarie, piuttosto che gettarle in un pool di autorelease esclusivamente per motivi di convenzione.

Alcuni altri miglioramenti includono l'azzeramento di riferimenti deboli, la copia automatica dei blocchi nell'heap, le accelerazioni su tutta la linea (6 volte per i pool di rilascio automatico!).

Discussioni più dettagliate su come funziona tutto ciò si trovano nei documenti LLVM su ARC.


2
-1 "ARC è una tecnologia di compilazione, a differenza di GC che è runtime e influirà negativamente sulle prestazioni". I conteggi dei riferimenti sono aumentati in fase di esecuzione, il che è molto inefficiente. Ecco perché tracciare GC come JVM e .NET è molto più veloce.
Jon Harrop,

1
@Jon: ne hai una prova? Dalla mia lettura, sembra che i nuovi algoritmi RC in genere funzionino bene o meglio di M&S GC.
xryl669,

1
@ xryl669: c'è una spiegazione completa nel Manuale GC ( gchandbook.org ). Nota che traccia! = M&S.
Jon Harrop,

3

Varia notevolmente dalla raccolta dei rifiuti. Hai visto gli avvisi che indicano che potresti perdere oggetti su linee diverse? Queste dichiarazioni ti dicono persino su quale linea hai assegnato l'oggetto. Questo è stato fatto un passo avanti e ora è possibile inserire retain/ releaseistruzioni nelle posizioni corrette, meglio della maggior parte dei programmatori, quasi il 100% delle volte. Di tanto in tanto ci sono alcune strane istanze di oggetti conservati con cui è necessario aiutarlo.


0

Molto ben spiegato dalla documentazione per gli sviluppatori Apple. Leggi "Come funziona ARC"

Per assicurarsi che le istanze non scompaiano mentre sono ancora necessarie, ARC tiene traccia di quante proprietà, costanti e variabili si riferiscono attualmente a ciascuna istanza della classe. ARC non dislocerà un'istanza fintanto che esiste almeno un riferimento attivo a quell'istanza.

Per assicurarsi che le istanze non scompaiano mentre sono ancora necessarie, ARC tiene traccia di quante proprietà, costanti e variabili si riferiscono attualmente a ciascuna istanza della classe. ARC non dislocerà un'istanza fintanto che esiste almeno un riferimento attivo a quell'istanza.

Conoscere il diff. tra Garbage Collection e ARC: leggi questo


0

ARC è una funzione di compilazione che fornisce la gestione automatica della memoria degli oggetti.

Invece di dover ricordare quando utilizzare retain, releasee autoreleaseARC valuta i requisiti di durata dei tuoi oggetti e inserisce automaticamente le chiamate di gestione della memoria appropriate al momento della compilazione. Il compilatore genera anche metodi dealloc appropriati per te.

Il compilatore inserisce le retain/releasechiamate necessarie al momento della compilazione, ma tali chiamate vengono eseguite in fase di esecuzione, proprio come qualsiasi altro codice.

Il seguente diagramma ti darebbe una migliore comprensione di come funziona ARC.

inserisci qui la descrizione dell'immagine

Coloro che sono nuovi nello sviluppo di iOS e che non hanno esperienza lavorativa sull'obiettivo C. Fare riferimento alla documentazione di Apple per la Guida alla programmazione della gestione avanzata della memoria per una migliore comprensione della gestione della memoria.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.