Як працює новий механізм автоматичного підрахунку довідок?


206

Чи може хтось коротко пояснити мені, як працює АРК? Я знаю, що це відрізняється від збирання сміття, але мені було просто цікаво, як саме це працює.

Крім того, якщо ARC робить те, що робить GC, не перешкоджаючи продуктивності, то чому Java використовує GC? Чому він також не використовує ARC?


2
Це розповість вам про це: http://clang.llvm.org/docs/AutomaticReferenceCounting.html Як це реалізовано в Xcode та iOS 5 під NDA.
Morten Fast

14
@mbehan Це погана порада. Я не хочу входити або навіть мати обліковий запис для iOS dev center, але мені все ж цікаво дізнатися про ARC.
Андрес Ф.

1
ARC не робить все, що робить GC, він вимагає від вас явної роботи з сильною і слабкою еталонною семантикою і просочує пам'ять, якщо ви не отримаєте це правильно. На мій досвід, це спочатку складно, коли ви використовуєте блоки в Objective-C, і навіть після того, як ви дізнаєтесь про хитрощі, вам залишається якийсь дратівливий (IMO) код кодового шаблону навколо багатьох звичаїв блоків. Зручніше просто забути про сильні / слабкі посилання. Більше того, GC може працювати трохи краще, ніж ARC Wrt. Процесор, але вимагає більше пам’яті. Це може бути швидше, ніж явне управління пам’яттю, коли у вас багато пам’яті.
TaylanUB

@TaylanUB: "потрібно більше пам'яті". Дуже багато людей говорять про це, але мені важко повірити.
Джон Харроп

2
@JonHarrop: В даний час я навіть не пам'ятаю, чому я це сказав, якщо чесно. :-) Тим часом я зрозумів, що існує стільки різних стратегій GC, що такі бланкетні висловлювання, мабуть, абсолютно нічого не варті. Дозвольте мені переказати Ганса Бома з його міфів і напівправди : «Чому ця область настільки схильна до сумнівних фольклорів?».
TaylanUB

Відповіді:


244

Кожен новий розробник, який приходить до Objective-C, повинен вивчити жорсткі правила, коли зберігати, випускати та автовипускати об’єкти. Ці правила навіть визначають умови іменування, які передбачають збереження кількості об'єктів, повернутих із методів. Управління пам’яттю в Objective-C стає другою природою, коли ви приймаєте ці правила до душі та послідовно застосовуєте їх, але час від часу навіть найдосвідченіші розробники какао піднімаються.

Завдяки Статичному аналізатору Clang, розробники LLVM зрозуміли, що ці правила є достатньо надійними, щоб вони могли створити інструмент для вказівки на витоки пам'яті та перезапускання в межах шляхів, які проходить ваш код.

Наступний логічний крок - автоматичний підрахунок посилань . Якщо компілятор може розпізнати, де ви повинні зберігати та випускати об’єкти, чому б вам не вставити цей код для вас? Жорсткі, повторювані завдання - це те, у чому складаються компілятори та їх брати. Люди забувають речі і роблять помилки, але комп'ютери набагато послідовніші.

Однак це не повністю позбавляє вас від турбот щодо управління пам'яттю на цих платформах. Я опишу основну проблему , щоб стежити за (зберегти цикли) в моїй обороні тут , що може зажадати трохи подумати про вашого боку , щоб відзначити слабкі покажчики. Однак це незначне порівняно з тим, що ви отримуєте в ARC.

У порівнянні з ручним управлінням пам’яттю та збиранням сміття, ARC дає вам найкраще з обох світів, вирізавши необхідність запису коду утримування / випуску, але не маючи профілів пам'яті зупинки та пилоподібних даних у середовищі зібраного сміття. Єдиними перевагами збору сміття перед цим є його здатність боротися із циклами утримування та той факт, що призначення атомних властивостей недороге (про що йдеться тут ). Я знаю, що замінюю всі існуючі коди Mac GC на реалізацію ARC.

Щодо того, чи можна це поширити на інші мови, схоже, це орієнтоване на систему відліку посилань у Objective-C. Це може бути важко застосувати до Java або інших мов, але я не знаю достатньо деталей компілятора низького рівня, щоб зробити там остаточну заяву. Зважаючи на те, що Apple докладає зусиль до LLVM, Objective-C стане першим, якщо інша сторона не докладе до цього значних власних ресурсів.

Представлення цього шокувало розробників на WWDC, тому люди не знали, що щось подібне можна зробити. Він може з’являтися на інших платформах з часом, але наразі це виключно для LLVM та Objective-C.


56
акцент мій: це не повністю
позбавляє

6
Чи справді ARC є нововведенням? З вашої відповіді я роблю висновок, що ARC - це нова концепція, яка використовується в Objective-C вперше (виправте мене, якщо я помиляюся). Якщо чесно, то я не розробник Objective-C і не знаю багато про ARC, але хіба Boost Shared Pointers (див. Boost.org) не зовсім те саме? А якщо їх немає, яка різниця?
theDmi

2
@DMM - Замість того, щоб покладатися на перевантажені оператори (як це робить Boost), це процес на рівні компілятора, який поширює його на всю мову. Крім усього іншого, це дозволяє легко перетворити додаток, що рахується вручну, в ARC. Boost також може обробляти локальні змінні інакше, ніж це робить ARC, де ARC знає момент, коли локальна змінна більше не використовується і може випустити в цій точці. Я вважаю, що з Boost вам все-таки потрібно певним чином вказати, що ви зробили зі змінною.
Бред Ларсон

6
Щоб відповісти на питання "чи це нове", Delphi вже більше десяти років має автоматичний підрахунок посилань для рядків, масивів та інтерфейсів (для підтримки COM). Я погоджуюся, що це дійсно приємний компроміс між середовищем gc'd та середовищем "зроби це все вручну". Я радий, що це в ObjC та LLVM (тому інші мови також можуть ним скористатися).
davidmw

2
@theDmi: "АРК насправді є нововведенням?". Автоматичний підрахунок посилань був винайдений в 1960 році і застосовувався в багатьох мовах, таких як Python і Mathematica. Він не використовується в JVM або CLR, оскільки він дуже повільний і протікає цикли.
Джон Харроп

25

ARC - це просто відтворити старий режим збереження / випуску (MRC), при цьому компілятор з'ясовує, коли зателефонувати зберегти / випустити. Це, як правило, має більш високу продуктивність, менший пік використання пам'яті та більш передбачувану продуктивність, ніж система GC.

З іншого боку, деякі типи структури даних неможливо з ARC (або MRC), тоді як GC може обробляти їх.

Наприклад, якщо у вас є клас з назвою node, а у node є NSArray дітей і одна посилання на його батьків, що "просто працює" з GC. З ARC (і з підрахунком посилань вручну) у вас є проблеми. Будь-який даний вузол буде посилатися від його дітей, а також від його батьків.

Подібно до:

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

Усе добре, коли ви використовуєте A (скажімо, через локальну змінну).

Коли ви закінчите з ним (і B1 / B2 / B3), система GC врешті вирішить переглянути все, що може знайти, починаючи з регістрів стека та процесора. Він ніколи не знайде A, B1, B2, B3, тому він доопрацює їх і переробить пам'ять в інші об’єкти.

Якщо ви використовуєте ARC або MRC, і закінчите з A, у нього буде знижка 3 (B1, B2 і B3 всі посилаються на нього), і B1 / B2 / B3 матиме посилання на 1 (A NSArray A містить одне посилання на кожен). Таким чином, всі ці об'єкти залишаються живими, хоча їх ніхто ніколи не може використовувати.

Загальне рішення - вирішити, що одна з цих посилань повинна бути слабкою (не сприяти підрахунку). Це буде працювати для деяких моделей використання, наприклад, якщо ви посилаєтесь на B1 / B2 / B3 лише через A. Однак у інших шаблонах це не вдається. Наприклад, якщо ви іноді будете триматися на B1, і очікуєте піднятися назад вгору через батьківський вказівник і знайдете А. Зі слабким посиланням, якщо ви тримаєтесь лише за B1, A може (і зазвичай буде) випаровуватися, і приймати B2, і B3 з цим.

Іноді це не проблема, але деякі дуже корисні та природні способи роботи зі складними структурами даних дуже важко використовувати з ARC / MRC.

Таким чином, ARC націлює однакові проблеми на цілі GC. Однак ARC працює на більш обмеженому наборі моделей використання, ніж GC, тож якщо ви взяли мову GC (наприклад, Java) і прищепили щось на зразок ARC на неї, деякі програми більше не працюватимуть (або, принаймні, генерують тонни занедбаної пам'яті , і може спричинити серйозні проблеми із заміною, або не вистачить пам’яті чи місця для обміну).

Ви також можете сказати, що ARC надає більшого пріоритету продуктивності (або, можливо, передбачуваності), тоді як GC надає більшого пріоритету тому, щоб бути загальним рішенням. В результаті GC має менші прогнозовані потреби в процесорі / пам'яті та більш низьку продуктивність (як правило), ніж ARC, але може працювати з будь-якою схемою використання. ARC буде працювати набагато краще для багатьох багатьох звичних моделей використання, але для кількох (дійсних!) Моделей використання він перепаде і загине.


"З іншого боку, деякі типи структури даних неможливі з ARC" Я думаю, ви мали на увазі, що автоматична очистка неможлива без підказок; очевидно, структури даних є.
Стівен Фішер

Звичайно, але ТОЛЬКО автоматична очистка об'єктів ObjC доступна під ARC, тому "немає автоматичної очистки" == "немає очищення". Я переформулюю тоді відповідь, коли в мене буде більше часу.
Смуги

@Stripes: еквівалент ручного очищення в ARC - це ручне переривання циклів, наприклад foo = nil.
Дуглас

"[ARC] прагне мати більш високу продуктивність ... ARC ставить більший пріоритет на продуктивність". Я здивовано читаю, що коли добре відомо, що підрахунок посилань відбувається набагато повільніше, ніж простеження збору сміття. flyingfrogblog.blogspot.co.uk/2011/01/…
Джон Харроп,

2
Теоретично GC відбувається швидше (кожна маніпуляція з підрахунком підрахунку повинна бути багатопроцесорною кешовою послідовністю, і їх дуже багато). На практиці єдина доступна система GC для ObjC набагато повільніше. Також надзвичайно часто для систем GC паузація потоків у випадкові години для сприйняття користувачем кількості часу (є деякі системи GC в реальному часі, але вони не є загальними, і я думаю, що вони мають "цікаві" обмеження)
Stripes

4

Магія

Але більш конкретно ARC працює, роблячи саме те, що ви зробили б зі своїм кодом (з певними незначними відмінностями). ARC - це технологія часу компіляції, на відміну від GC, яка виконує і негативно впливатиме на вашу продуктивність. ARC буде відслідковувати посилання на об'єкти для вас і синтезувати методи утримування / випуску / автовивільнення відповідно до звичайних правил. Через це ARC також може випустити речі, як тільки вони більше не потрібні, а не кидати їх у пул автовипуску виключно заради домовленостей.

Деякі інші вдосконалення включають нульові слабкі посилання, автоматичне копіювання блоків у купу, прискорення роботи по всій дошці (6 разів для пулів автоматичного випуску!).

Більш детальна дискусія про те, як все це працює, можна знайти в Документах LLVM на ARC.


2
-1 "ARC - це технологія часу компіляції, на відміну від GC, яка виконує час і негативно вплине на вашу продуктивність". Довідкові підрахунки стикаються під час виконання, що дуже неефективно. Ось чому відстеження GC, таких як JVM та .NET, відбувається набагато швидше.
Джон Харроп

1
@Jon: У вас є докази цього? З мого власного читання, схоже, що нові алгоритми RC зазвичай ефективніше або краще, ніж M&S GC.
xryl669

1
@ xryl669: В Посібнику з ГК є повне пояснення ( gchandbook.org ). Зауважте, що трасування! = M&S.
Джон Харроп

3

Він сильно відрізняється від збору сміття. Чи бачили ви попередження, які говорять про те, що, можливо, витікаєте предмети на різних лініях? Ці висловлювання навіть повідомляють вам, у якому рядку ви виділили об'єкт. Це було зроблено на крок далі, і тепер можна вставляти retain/ releaseзаяви в належних місцях, краще, ніж більшість програмістів, майже в 100% часу. Інколи трапляються деякі дивні екземпляри збережених об'єктів, з якими вам потрібно допомогти.


0

Дуже добре пояснюється документацією для розробників Apple. Читайте "Як працює ARC"

Щоб переконатися, що екземпляри не зникають, поки вони все ще потрібні, ARC відстежує, скільки властивостей, констант і змінних в даний час посилаються на кожен екземпляр класу. ARC не буде розміщувати примірник до тих пір, поки щонайменше одна активна посилання на цей екземпляр ще існує.

Щоб переконатися, що екземпляри не зникають, поки вони все ще потрібні, ARC відстежує, скільки властивостей, констант і змінних в даний час посилаються на кожен екземпляр класу. ARC не буде розміщувати примірник до тих пір, поки щонайменше одна активна посилання на цей екземпляр ще існує.

Щоб знати Діфф. між збиранням сміття та ARC: прочитайте це


0

ARC - це компіляторна функція, яка забезпечує автоматичне управління пам'яттю об'єктів.

Замість того, щоб вам потрібно було пам’ятати, коли їх використовувати retain, release, autoreleaseARC оцінює вимоги до експлуатації об’єктів і автоматично вставляє відповідні виклики управління пам’яттю для вас під час компіляції. Компілятор також генерує для вас відповідні методи взаємодії.

Компілятор вставляє необхідні retain/releaseдзвінки під час компіляції, але ці виклики виконуються під час виконання, як і будь-який інший код.

Наступна діаграма допоможе вам краще зрозуміти, як працює АРК.

введіть тут опис зображення

Тим, хто у розробці iOS не знайомий і не має досвіду роботи на Цілі C. Будь ласка, зверніться до документації Apple для Посібника з програмування розширеного управління пам’яттю для кращого розуміння управління пам’яттю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.