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

206

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

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

user635064
источник
2
Это расскажет вам все об этом: http://clang.llvm.org/docs/AutomaticReferenceCounting.html Как это реализовано в Xcode и iOS 5 под NDA.
Мортен Фаст
14
@mbehan Это плохой совет. Я не хочу входить в систему или даже иметь учетную запись для iOS dev center, но, тем не менее, мне интересно знать об ARC.
Андрес Ф.
1
ARC не делает всего того, что делает GC, он требует от вас явной работы с сильной и слабой ссылочной семантикой и приводит к утечке памяти, если вы не понимаете их правильно. По моему опыту, это поначалу сложно, когда вы используете блоки в Objective-C, и даже после того, как вы узнаете о хитростях, у вас останется какой-то надоедливый (IMO) шаблонный код для многих типов использования блоков. Удобнее просто забыть о сильных / слабых ссылках. Более того, GC может работать несколько лучше, чем ARC. Процессор, но требует больше памяти. Это может быть быстрее, чем явное управление памятью, когда у вас много памяти.
TaylanUB
@TaylanUB: «требуется больше памяти». Многие так говорят, но мне трудно в это поверить.
Джон Харроп
2
@JonHarrop: В настоящее время я даже не помню, почему я так сказал, если честно. :-) Тем временем я понял, что существует так много разных стратегий GC, что такие общие заявления, вероятно, бесполезны. Позвольте мне рассказать Ганса Бема из его мифов и полуправд о распределении памяти : «Почему эта область так подвержена сомнительным народным мудростям?»
TaylanUB

Ответы:

244

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

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

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

Однако это не освобождает вас от необходимости беспокоиться об управлении памятью на этих платформах. Я опишу основную проблему , чтобы следить за (сохранить циклы) в моем ответе здесь , что может потребовать немного подумать о вашей стороны , чтобы отметить слабые указатели. Однако это незначительно по сравнению с тем, что вы получаете в 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: «Действительно ли ARC - это инновация?». Автоматический подсчет ссылок был изобретен в 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 (NSArray A содержит одну ссылку на каждый). Таким образом, все эти объекты остаются живыми, хотя ничто не может их использовать.

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

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

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

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

полосы
источник
«С другой стороны, некоторые типы структуры данных невозможны с ARC» Я думаю, вы имели в виду, что автоматическая очистка невозможна без подсказок; очевидно, структуры данных.
Стивен Фишер
Конечно, но в ARC доступна ТОЛЬКО автоматическая очистка объектов ObjC, поэтому «нет автоматической очистки» == «нет очистки». Я перефразирую и отвечу, когда у меня будет больше времени.
Полосы
@Stripes: эквивалентом ручной очистки в ARC является ручное прерывание циклов, например foo = nil.
Дуглас
«[ARC], как правило, будет иметь более высокую производительность ... ARC придает большее значение производительности». Я удивлен, когда узнаю, что подсчет ссылок намного медленнее, чем отслеживание сборки мусора. flyingfrogblog.blogspot.co.uk/2011/01/…
Джон Харроп
2
Теоретически GC работает быстрее (каждая манипуляция счетчиком ссылок должна быть согласована с многопроцессорным кэшем, и их много). На практике единственная доступная система GC для ObjC намного медленнее. Также для систем GC очень часто приходится приостанавливать потоки в случайное время на ощутимое для пользователя количество времени (есть некоторые системы GC в реальном времени, но они не являются общими, и я думаю, что у них есть «интересные» ограничения)
Stripes
4

магия

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

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

Более подробное обсуждение того, как все это работает, можно найти в Документах LLVM по ARC.

Джошуа Вайнберг
источник
2
-1 «ARC - это технология времени компиляции, в отличие от GC, которая является средой выполнения и отрицательно скажется на вашей производительности». Подсчет ссылок увеличивается во время выполнения, что очень неэффективно. Вот почему отслеживание GC, таких как JVM и .NET, происходит намного быстрее.
Джон Харроп
1
@Jon: у вас есть доказательства этого? Из моего собственного чтения кажется, что новые алгоритмы RC обычно работают так же хорошо или лучше, чем M & S GC.
xryl669
1
@ xryl669: Полное объяснение приведено в Руководстве GC ( gchandbook.org ). Обратите внимание, что трассировка! = M & S.
Джон Харроп
3

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

FreeAsInBeer
источник
0

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

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

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

Чтобы узнать Diff. между сборкой мусора и ARC: прочитайте это

Лалит Кумар
источник
0

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

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

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

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

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

Те, кто новичок в разработке для iOS и не имеют опыта работы с Objective C. Пожалуйста, обратитесь к документации Apple для Руководства по программированию Advanced Memory Management для лучшего понимания управления памятью.

Йогеш Бхарате
источник