Как работает новый механизм автоматического подсчета ссылок?

Может кто-нибудь кратко объяснить мне, как работает ARC? Я знаю, что это отличается от Garbage Collection, но мне просто интересно, как это работает.

Кроме того, если ARC делает то, что делает GC, не мешая производительности, то почему Java использует GC? Почему он не использует ARC?

Ответ 1

Каждый новый разработчик, который приходит в Objective-C, должен узнать жесткие правила сохранения, выпуска и создания объектов авторекламы. Эти правила даже определяют соглашения об именах, которые подразумевают сохранение количества объектов, возвращаемых из методов. Управление памятью в Objective-C становится второй натурой после того, как вы примете эти правила к сердцу и примените их последовательно, но даже самые опытные разработчики Cocoa время от времени выскакивают.

С помощью статического анализатора Clang разработчики LLVM поняли, что эти правила были достаточно надежными, чтобы они могли создавать инструмент, чтобы указывать на утечки памяти и переопределения в пределах путей, которые ваш код принимает.

Автоматический подсчет ссылок (ARC) - следующий логический шаг. Если компилятор может распознать, где вы должны сохранять и отпускать объекты, почему бы вам не вставить этот код для вас? Жесткие, повторяющиеся задачи - это то, что компиляторы и их братья велики. Люди забывают о вещах и делают ошибки, но компьютеры гораздо более последовательны.

Однако это не полностью освобождает вас от беспокойства по поводу управления памятью на этих платформах. Я описываю основную проблему, чтобы следить за (удерживать циклы) в моем ответе здесь, что может потребовать небольшой мысли с вашей стороны, чтобы отметить слабые указатели. Однако это незначительное по сравнению с тем, что вы получаете в ARC.

По сравнению с ручным управлением памятью и сборкой мусора, ARC дает вам лучшее из обоих миров, вырезая необходимость писать код сохранения/выпуска, но не имея профилей памяти для останова и пилообразной формы, видимых в среде сбора мусора. О единственных преимуществах сборки мусора над этим есть его способность справляться с циклами сохранения и тот факт, что присвоения атомных свойств недороги (как обсуждалось здесь), Я знаю, что я заменяю весь мой существующий Mac GC-код реализацией ARC.

Что касается того, может ли это быть распространено на другие языки, оно, похоже, ориентировано вокруг системы подсчета ссылок в Objective-C. Это может быть сложно применить к Java или другим языкам, но я не знаю достаточно о деталях компилятора низкого уровня, чтобы сделать там окончательное утверждение. Учитывая, что Apple является тем, кто продвигает это усилие в LLVM, Objective-C наступит первым, если другая сторона не возьмет на себя значительные ресурсы для этого.

Открытие этих шокированных разработчиков на WWDC, поэтому люди не знали, что что-то подобное можно сделать. Он может появляться на других платформах с течением времени, но теперь он исключительно для LLVM и Objective-C.

Ответ 2

ARC просто воспроизводит старое сохранение/освобождение (MRC), когда компилятор вычисляет, когда нужно вызвать сохранение/освобождение. Он будет иметь более высокую производительность, более низкое использование памяти в пике и более предсказуемую производительность, чем система GC.

С другой стороны, некоторые типы структуры данных невозможны с ARC (или MRC), в то время как GC может обрабатывать их.

В качестве примера, если у вас есть класс с именем node, а node имеет NSArray для детей и единственную ссылку на родителя, которая "просто работает" с GC. С ARC (и с ручной подсчетом ссылок) у вас есть проблема. Любые данные node будут ссылаться на его дочерние элементы, а также на их родителей.

Как

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 имеет одна ссылка для каждого). Таким образом, все эти объекты остаются в живых, хотя ничто не может их использовать.

Общее решение заключается в том, чтобы решить, что одна из этих ссылок должна быть слабой (не способствующей подсчету ссылок). Это будет работать для некоторых шаблонов использования, например, если вы ссылаетесь на B1/B2/B3 только через A. Однако в других шаблонах он терпит неудачу. Например, если вы иногда держитесь за B1 и ожидаете вернуться обратно через родительский указатель и найдите A. При слабой ссылке, если вы удерживаете только B1, A может (и обычно будет) испаряться и принимать B2 и B3 с ним.

Иногда это не проблема, но очень полезные и естественные способы работы со сложными структурами данных очень сложно использовать с ARC/MRC.

Таким образом, ARC нацеливает те же задачи GC-целей. Однако ARC работает с более ограниченным набором шаблонов использования, а затем GC, поэтому, если вы взяли язык GC (например, Java) и перенесли что-то вроде ARC на него, некоторые программы больше не работали (или, по крайней мере, генерировали бы тонны оставленной памяти, и может вызвать серьезные проблемы с обменом или нехватку памяти или место подкачки).

Вы также можете сказать, что ARC ставит больший приоритет производительности (или, возможно, предсказуемости), в то время как GC ставит больший приоритет в качестве общего решения. В результате GC имеет менее предсказуемые требования к ЦП/памяти и более низкую производительность (обычно), чем ARC, но может обрабатывать любой шаблон использования. ARC будет работать намного лучше для многих распространенных шаблонов использования, но для нескольких (действующих!) Шаблонов использования он упадет и умрет.

Ответ 3

Магия

Но более конкретно ARC работает, делая именно то, что вы будете делать с вашим кодом (с некоторыми незначительными отличиями). ARC - это технология времени компиляции, в отличие от GC, которая является временем выполнения и негативно повлияет на вашу производительность. ARC будет отслеживать ссылки на объекты для вас и синтезировать методы сохранения/выпуска/авторекламы в соответствии с обычными правилами. Из-за этого ARC может также выпускать вещи, как только они больше не нужны, вместо того, чтобы бросать их в пул авторекламы исключительно ради соглашения.

Некоторые другие улучшения включают обнуление слабых ссылок, автоматическое копирование блоков в кучу, ускорение по всем направлениям (6x для пулов автоопределения!).

Более подробное обсуждение того, как все это работает, содержится в LLVM Docs в ARC.

Ответ 4

Он сильно отличается от сбора мусора. Вы видели предупреждения, которые говорят вам, что вы можете протекать объекты на разных линиях? Эти утверждения даже сообщают вам, в какой строке вы выделили объект. Это сделало еще один шаг и теперь может вставлять операторы retain/release в правильные местоположения, лучше, чем большинство программистов, почти в 100% случаев. Иногда есть некоторые странные экземпляры сохраняемых объектов, которые вам нужно помочь.

Ответ 5

Очень хорошо объясняется документацией разработчика Apple. Прочитайте "Как работает ARC"

Чтобы убедиться, что экземпляры не исчезнут, пока они еще нужны, ARC отслеживает, сколько свойств, констант и переменных в настоящее время ссылается на каждый экземпляр класса. ARC не освободит экземпляр экземпляра, если по крайней мере одна активная ссылка на этот экземпляр все еще существует.

Чтобы убедиться, что экземпляры не исчезнут, пока они еще нужны, ARC отслеживает, сколько свойств, констант и переменных в настоящее время ссылается на каждый экземпляр класса. ARC не освободит экземпляр экземпляра, если по крайней мере одна активная ссылка на этот экземпляр все еще существует.

Знать Diff. между сборкой мусора и ARC: прочитайте this

Ответ 6

ARC - это функция компилятора, которая обеспечивает автоматическое управление памятью объектов.

Вместо того, чтобы вам autorelease помнить, когда следует использовать retain, release и autorelease, ARC оценивает требования к autorelease службы ваших объектов и автоматически вставляет вам соответствующие вызовы управления памятью во время компиляции. Компилятор также генерирует подходящие методы dealloc для вас.

Компилятор вставляет необходимые вызовы retain/release во время компиляции, но эти вызовы выполняются во время выполнения, как и любой другой код.

Следующая диаграмма даст вам лучшее понимание того, как работает ARC.

enter image description here