Jak działa nowy mechanizm automatycznego zliczania referencji?

206

Czy ktoś może mi krótko wyjaśnić, jak działa ARC? Wiem, że różni się od Garbage Collection, ale zastanawiałem się dokładnie, jak to działa.

Ponadto, jeśli ARC robi to, co robi GC bez ograniczania wydajności, to dlaczego Java używa GC? Dlaczego nie używa również ARC?

użytkownik635064
źródło
2
Dzięki temu dowiesz się wszystkiego: http://clang.llvm.org/docs/AutomaticReferenceCounting.html Jak to jest zaimplementowane w Xcode i iOS 5 pod NDA.
Morten Fast
14
@mbehan To słaba rada. Nie chcę się logować ani nawet mieć konta w centrum deweloperów iOS, ale mimo to interesuje mnie wiedza o ARC.
Andres F.,
1
ARC nie robi wszystkiego, co robi GC, wymaga jawnej pracy z silną i słabą semantyką odniesień i wycieka pamięć, jeśli nie masz racji. Z mojego doświadczenia wynika, że na początku jest to trudne, gdy używasz bloków w Objective-C, a nawet po tym, jak poznajesz sztuczki, masz trochę irytującego (IMO) kodu zawierającego wiele zastosowań bloków. Wygodniej jest po prostu zapomnieć o mocnych / słabych referencjach. Co więcej, GC może działać nieco lepiej niż ARC wrt. Procesor, ale wymaga więcej pamięci. Może być szybsze niż jawne zarządzanie pamięcią, gdy masz dużo pamięci.
TaylanUB
@TaylanUB: „wymaga więcej pamięci”. Wiele osób tak mówi, ale trudno mi w to uwierzyć.
Jon Harrop
2
@JonHarrop: Szczerze mówiąc, obecnie nawet nie pamiętam, dlaczego to powiedziałem. :-) Tymczasem zdałem sobie sprawę, że istnieje tak wiele różnych strategii GC, że takie ogólne instrukcje są prawdopodobnie bezwartościowe. Pozwólcie, że wyrecytuję Hansa Boehm'a z jego mitów alokacji pamięci i półprawd : „Dlaczego ten obszar jest tak podatny na wątpliwe mądrości ludowe?”
TaylanUB

Odpowiedzi:

244

Każdy nowy programista, który przyjdzie do Objective-C, musi nauczyć się sztywnych zasad, kiedy zachować, zwolnić i automatycznie wydać obiekt. Reguły te określają nawet konwencje nazewnictwa, które implikują zachowanie liczby obiektów zwracanych z metod. Zarządzanie pamięcią w Objective-C staje się drugą naturą, gdy weźmiesz sobie do serca te zasady i konsekwentnie je zastosujesz, ale nawet najbardziej doświadczeni twórcy kakao od czasu do czasu tracą kontrolę.

Dzięki analizatorowi statycznemu Clanga programiści LLVM zdali sobie sprawę, że reguły te są na tyle niezawodne, że mogą zbudować narzędzie do wskazywania wycieków pamięci i nadmiernego wydawania w ścieżkach, które podąża Twój kod.

Kolejnym logicznym krokiem jest automatyczne zliczanie referencji (ARC). Jeśli kompilator rozpoznaje, gdzie należy zachować i zwalniać obiekty, dlaczego nie wstawić tego kodu? Sztywne, powtarzalne zadania są tym, w czym świetnie radzą sobie kompilatorzy i ich bracia. Ludzie zapominają rzeczy i popełniają błędy, ale komputery są znacznie bardziej spójne.

Nie oznacza to jednak, że nie musisz martwić się o zarządzanie pamięcią na tych platformach. Opiszę podstawową kwestię zwrócić uwagę (zachowują cykli) w moją odpowiedź tutaj , co może wymagać trochę myśli na części, aby oznaczyć słabe wskaźniki. Jest to jednak niewielkie w porównaniu do tego, co zyskujesz w ARC.

W porównaniu z ręcznym zarządzaniem pamięcią i zbieraniem pamięci, ARC daje to, co najlepsze z obu światów, eliminując potrzebę pisania kodu zatrzymania / zwolnienia, ale nie widząc profili pamięci zatrzymania i piłokształtnych w środowisku śmieci. Jedynymi zaletami wyrzucania elementów bezużytecznych jest możliwość radzenia sobie z cyklami przechowywania oraz fakt, że przypisywanie właściwości atomowych jest niedrogie (jak omówiono tutaj ). Wiem, że zastępuję cały istniejący kod Mac GC implementacjami ARC.

Jeśli chodzi o to, czy można go rozszerzyć na inne języki, wydaje się, że jest ukierunkowany na system liczenia referencji w Objective-C. Może to być trudne do zastosowania w Javie lub innych językach, ale nie znam wystarczająco dużo szczegółów na temat kompilatora niskiego poziomu, aby dokonać tam ostatecznego stwierdzenia. Biorąc pod uwagę, że Apple jest tym, który popycha ten wysiłek w LLVM, cel C będzie najważniejszy, chyba że inna strona zaangażuje w to znaczne zasoby.

Odsłonięcie tego zszokowanego programisty w WWDC, więc ludzie nie byli świadomi, że coś takiego można zrobić. Z czasem może pojawiać się na innych platformach, ale na razie jest wyłączny dla LLVM i Objective-C.

Brad Larson
źródło
56
moje podkreślenie: to nie całkowicie uwalnia cię od martwienia się o zarządzanie pamięcią
bshirley
6
Czy ARC to naprawdę innowacja? Z twojej odpowiedzi wnioskuję, że ARC jest nową koncepcją, która jest używana w Objective-C po raz pierwszy (popraw mnie, jeśli się mylę). Szczerze mówiąc, nie jestem programistą Objective-C i nie wiem dużo o ARC, ale czy Boost Shared Pointers (patrz boost.org) nie jest dokładnie tym samym? A jeśli nie są, jaka jest różnica?
theDmi
2
@DMM - Zamiast polegać na przeciążonych operatorach (podobnie jak Boost), jest to proces na poziomie kompilatora, który rozszerza go na cały język. Ułatwia to między innymi konwersję ręcznie zliczanej referencji na ARC. Zwiększenie może również obsługiwać zmienne lokalne inaczej niż ARC, gdzie ARC wie, kiedy zmienna lokalna nie jest już używana i może w tym momencie zwolnić. Uważam, że dzięki funkcji Boost nadal musisz w jakiś sposób określić, że dokonałeś zmiany ze zmienną.
Brad Larson
6
Aby odpowiedzieć na pytanie „czy to jest nowe”, Delphi ma automatyczne liczenie referencji dla łańcuchów, tablic i interfejsów (do obsługi COM) od ponad dekady. Zgadzam się, że to naprawdę dobry kompromis między środowiskiem gc'd a środowiskiem „zrób to wszystko ręcznie”. Cieszę się, że jest w ObjC i LLVM (więc inne języki również mogą z niego skorzystać).
davidmw
2
@theDmi: „Czy ARC to naprawdę innowacja?”. Automatyczne liczenie referencji zostało wynalezione w 1960 roku i było używane w wielu językach, takich jak Python i Mathematica. Nie jest stosowany w JVM ani CLR, ponieważ jest bardzo wolny i cykle wycieków.
Jon Harrop
25

ARC po prostu odtwarza stare zachowanie / zwolnienie (MRC), a kompilator zastanawia się, kiedy wywołać zachowanie / zwolnienie. Będzie miał tendencję do wyższej wydajności, niższego zużycia pamięci szczytowej i bardziej przewidywalnej wydajności niż system GC.

Z drugiej strony niektóre typy struktury danych nie są możliwe w przypadku ARC (lub MRC), podczas gdy GC może je obsłużyć.

Na przykład, jeśli masz klasę o nazwie węzeł, a węzeł ma macierz potomną NSA i pojedyncze odniesienie do jej elementu nadrzędnego, który „po prostu działa” z GC. Z ARC (i manualnym zliczaniem referencji) masz problem. Każdy węzeł będzie przywoływany zarówno od jego dzieci, jak i od jego rodzica.

Lubić:

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

Wszystko jest w porządku, gdy używasz A (powiedzmy przez zmienną lokalną).

Kiedy to zrobisz (i B1 / B2 / B3), system GC ostatecznie zdecyduje się spojrzeć na wszystko, co może znaleźć, począwszy od stosu i rejestrów procesora. Nigdy nie znajdzie A, B1, B2, B3, więc sfinalizuje je i przetworzy pamięć na inne obiekty.

Gdy użyjesz ARC lub MRC i skończysz z A, jego liczba zwrotna wynosi 3 (B1, B2 i B3 odnoszą się do niego), a B1 / B2 / B3 będą miały liczbę referencyjną równą 1 (NSArray A zawiera jedno odniesienie do każdy). Tak więc wszystkie te obiekty pozostają żywe, mimo że nic nigdy nie może ich użyć.

Częstym rozwiązaniem jest stwierdzenie, że jedno z tych odniesień musi być słabe (nie przyczyniać się do liczenia odniesień). Będzie to działać w przypadku niektórych wzorców użytkowania, na przykład, jeśli odwołujesz się do B1 / B2 / B3 tylko przez A. Jednak w innych wzorcach nie działa. Na przykład, jeśli czasami trzymasz się B1 i spodziewasz się wspiąć przez wskaźnik nadrzędny i znaleźć A. Słabe odniesienie, jeśli tylko trzymasz się B1, A może (i normalnie) wyparuje i weź B2 i B3 z tym.

Czasami nie jest to problem, ale bardzo przydatne i naturalne sposoby pracy ze złożonymi strukturami danych są bardzo trudne w użyciu z ARC / MRC.

Tak więc ARC atakuje ten sam rodzaj problemów, co cele GC. Jednak ARC działa na bardziej ograniczonym zestawie wzorców użytkowania niż GC, więc jeśli weźmiesz język GC (jak Java) i wszczepisz na nim coś takiego jak ARC, niektóre programy już nie będą działać (lub przynajmniej wygenerują tony porzuconej pamięci i może powodować poważne problemy z zamianą lub brak pamięci lub przestrzeni wymiany).

Można również powiedzieć, że ARC kładzie większy nacisk na wydajność (a może przewidywalność), podczas gdy GC kładzie większy nacisk na bycie rozwiązaniem ogólnym. W rezultacie GC ma mniej przewidywalne zapotrzebowanie na procesor / pamięć i niższą wydajność (zwykle) niż ARC, ale może obsłużyć dowolny wzorzec użytkowania. ARC będzie działać znacznie lepiej w przypadku wielu powszechnych wzorców użytkowania, ale w przypadku kilku (prawidłowych!) Wzorców przewróci się i umrze.

Paski
źródło
„Z drugiej strony niektóre typy struktury danych nie są możliwe w przypadku ARC”. Myślę, że miałeś na myśli, że automatyczne czyszczenie nie jest możliwe bez wskazówek; oczywiście są struktury danych.
Steven Fisher
Jasne, ale TYLKO automatyczne czyszczenie obiektów ObjC jest dostępne pod ARC, więc „brak automatycznego czyszczenia” == „brak czyszczenia”. Przeredaguję, a potem odpowiem, kiedy będę miał więcej czasu.
Stripes
@Stripes: odpowiednikiem ręcznego czyszczenia w ARC jest ręczne przerywanie cykli, np foo = nil.
Douglas,
„[ARC] będzie miało wyższą wydajność… ARC kładzie większy nacisk na wydajność”. Jestem zaskoczony czytając, że kiedy wiadomo, liczenie referencji jest znacznie wolniejsze niż śledzenie śmieci. flyingfrogblog.blogspot.co.uk/2011/01/…
Jon Harrop
2
Teoretycznie GC jest szybszy (każda manipulacja zliczaniem referencji musi być spójna z pamięcią podręczną wieloprocesorową, a jest ich dużo). W praktyce jedyny dostępny system GC dla ObjC jest znacznie wolniejszy. Niezwykle często systemy GC przerywają wątki w przypadkowych momentach, aby użytkownik odczuł czas (istnieje kilka systemów GC w czasie rzeczywistym, ale nie są one powszechne i myślę, że mają „interesujące” ograniczenia)
Stripes
4

magia

Ale dokładniej ARC działa, robiąc dokładnie to, co zrobiłbyś ze swoim kodem (z pewnymi drobnymi różnicami). ARC to technologia czasu kompilacji, w przeciwieństwie do GC, który jest środowiskiem uruchomieniowym i negatywnie wpłynie na wydajność. ARC będzie śledzić odniesienia do obiektów dla Ciebie i zsyntetyzować metody zachowania / wydania / automatycznego wydawania zgodnie z normalnymi zasadami. Z tego powodu ARC może także wypuszczać rzeczy, gdy tylko nie są już potrzebne, zamiast wrzucać je do puli autorelease wyłącznie dla celów konwencji.

Niektóre inne ulepszenia obejmują zerowanie słabych referencji, automatyczne kopiowanie bloków na stos, przyspieszenie na całym forum (6x dla pul autorelease!).

Bardziej szczegółowa dyskusja na temat tego, jak to wszystko działa, znajduje się w Dokumentach LLVM na ARC.

Joshua Weinberg
źródło
2
-1 „ARC to technologia czasu kompilacji, w przeciwieństwie do GC, który jest środowiskiem uruchomieniowym i wpłynie negatywnie na wydajność”. Liczniki referencyjne są podbijane w czasie wykonywania, co jest bardzo nieefektywne. Dlatego śledzenie GC, takich jak JVM i .NET, jest o wiele szybsze.
Jon Harrop
1
@Jon: Czy masz na to dowód? Z mojego własnego czytania wynika, że ​​nowe algorytmy RC zwykle działają tak samo lub lepiej niż M&S GC.
xryl669
1
@ xryl669: Pełne wyjaśnienie znajduje się w Podręczniku GC ( gchandbook.org ). Zauważ, że śledzenie! = M&S.
Jon Harrop,
3

Różni się znacznie od śmiecia. Czy widziałeś ostrzeżenia, które mówią, że możesz przeciekać przedmioty na różnych liniach? Te instrukcje mówią nawet, w której linii przypisałeś obiekt. Zostało to zrobione krok dalej i teraz można wstawiać retain/ releaseinstrukcje w odpowiednich miejscach, lepiej niż większość programistów, prawie w 100% przypadków. Czasami zdarzają się dziwne przypadki zachowanych obiektów, z którymi musisz sobie pomóc.

FreeAsInBeer
źródło
0

Bardzo dobrze wyjaśnione w dokumentacji dla deweloperów Apple. Przeczytaj „Jak działa ARC”

Aby mieć pewność, że instancje nie znikną, gdy są nadal potrzebne, ARC śledzi, ile właściwości, stałych i zmiennych odnosi się obecnie do każdej instancji klasy. ARC nie cofnie przydziału wystąpienia, dopóki istnieje co najmniej jedno aktywne odwołanie do tego wystąpienia.

Aby mieć pewność, że instancje nie znikną, gdy są nadal potrzebne, ARC śledzi, ile właściwości, stałych i zmiennych odnosi się obecnie do każdej instancji klasy. ARC nie cofnie przydziału wystąpienia, dopóki istnieje co najmniej jedno aktywne odwołanie do tego wystąpienia.

Poznać Diff. między Garbage collection a ARC: Przeczytaj to

Lalit Kumar
źródło
0

ARC to funkcja kompilatora, która zapewnia automatyczne zarządzanie pamięcią obiektów.

Zamiast pamiętać, kiedy używać retain, release, autoreleaseARC ocenia wymagania dotyczące żywotności obiektów i automatycznie wstawia odpowiednie wezwania do zarządzania pamięcią w czasie kompilacji. Kompilator generuje również odpowiednie metody dealloc.

Kompilator wstawia niezbędne retain/releasewywołania w czasie kompilacji, ale te wywołania są wykonywane w czasie wykonywania, tak jak każdy inny kod.

Poniższy schemat daje lepsze zrozumienie działania ARC.

wprowadź opis zdjęcia tutaj

Ci, którzy są nowi w tworzeniu systemu iOS i nie mają doświadczenia w pracy z celem C. Aby zapoznać się z zarządzaniem pamięcią, zapoznaj się z dokumentacją Apple w Przewodniku programowania zaawansowanego zarządzania pamięcią.

rev Yogesh Bharate
źródło