新的自动参考计数机制如何工作?


206

有人可以简要向我解释ARC的工作原理吗?我知道它与垃圾收集有所不同,但是我只是想知道它是如何工作的。

此外,如果ARC在不影响性能的情况下执行GC的功能,那么Java为什么要使用GC?为什么还不使用ARC?


2
这将告诉您所有相关信息:http : //clang.llvm.org/docs/AutomaticReferenceCounting.html在Xcode和iOS 5中如何实现它在NDA下。
Morten Fast,

14
@mbehan这是一个糟糕的建议。我不想登录,甚至不想拥有iOS开发中心的帐户,但是我仍然对了解ARC感兴趣。
安德列斯·F

1
ARC不能完成GC的所有工作,它要求您显式地使用强引用和弱引用语义,如果使用不正确,则会泄漏内存。以我的经验,当您在Objective-C中使用块时,这首先是棘手的,甚至在您了解了这些窍门之后,您仍然会得到一些烦人的(IMO)样板代码,涉及许多块的用法。忘记强/弱引用会更方便。而且,GC的性能可能比ARC wrt好。CPU,但需要更多内存。当您有大量内存时,它可能比显式内存管理更快。
TaylanUB

@TaylanUB:“需要更多内存”。很多人这么说,但我很难相信。
乔恩·哈罗普

2
@JonHarrop:老实说,目前我什至不记得我为什么这么说。:-)同时,我意识到,有许多不同的GC策略,以至于这些笼统的声明可能一文不值。让我从记忆分配的神话和半真相中背诵汉斯·博姆:“为什么这个地区容易受到可疑的民间智慧的影响?”
TaylanUB 2014年

Answers:


244

每个使用Objective-C的新开发人员都必须学习何时保留,释放和自动释放对象的严格规则。这些规则甚至指定了命名约定,这些约定暗示从方法返回的对象的保留计数。一旦您牢记这些规则并一致地应用它们,Objective-C中的内存管理就成为了第二天性,但是即使是最有经验的Cocoa开发人员也会时不时地滑倒。

借助Clang静态分析器,LLVM开发人员意识到这些规则足够可靠,因此他们可以构建一个工具来指出代码采用的路径中的内存泄漏和过高释放。

自动参考计数(ARC)是下一个逻辑步骤。如果编译器可以识别应保留和释放对象的位置,为什么不让它为您插入该代码呢?刚性的,重复性的任务是编译器及其兄弟们擅长的。人们会忘记事情并犯错误,但是计算机的一致性更高。

但是,这并不能完全让您不必担心这些平台上的内存管理。我在这里的回答中描述了要注意的(保留周期)主要问题,这可能需要您一些思考以标记弱指针。但是,与您在ARC中获得的相比,这是次要的。

与手动内存管理和垃圾回收相比,ARC通过消除编写保留/释放代码的需要,为您提供了两全其美的优势,但又没有垃圾回收环境中出现的暂停和锯齿式内存配置文件。现在唯一的优势垃圾收集有超过这个是它对付保留周期,事实上,原子属性分配是廉价的(所讨论的能力在这里)。我知道我将所有现有的Mac GC代码替换为ARC实现。

至于是否可以将其扩展到其他语言,似乎围绕着Objective-C中的引用计数系统。将其应用于Java或其他语言可能很困难,但是我对低级编译器的详细信息了解不足,无法在那里做出明确的声明。鉴于苹果公司是在LLVM中推动这一努力的人,除非有另一方为此投入大量资源,否则Objective-C将排在第一位。

WWDC上的开发人员震惊的揭幕,所以人们并不知道可以做这样的事情。随着时间的流逝,它可能会出现在其他平台上,但目前它是LLVM和Objective-C专有的。


56
强调我的:这并不能完全让您不必担心内存管理
bshirley

6
ARC真的是一项创新吗?根据您的回答,我得出结论说ARC是一个新概念,这是第一次在Objective-C中使用(如果我错了,请纠正我)。老实说,我不是Objective-C开发人员,对ARC也不了解很多,但是Boost Shared Pointers(请参阅boost.org)不是一回事吗?如果不是,那有什么区别?
theDmi 2011年

2
@DMM-这不是编译器级的过程,而不是依靠重载的运算符(如Boost那样),它将其扩展到整个语言。除其他外,这使得将手动引用计数的应用程序转换为ARC变得容易。Boost可能也与ARC不同,对局部变量的处理方式不同,在ARC中,ARC知道当下不再使用局部变量并且可以在此时释放它。我相信,使用Boost仍然需要以某种方式指定变量已完成。
布拉德·拉尔森

6
为了回答“是新问题”的问题,十多年来,Delphi已经对字符串,数组和接口(用于COM支持)进行了自动引用计数。我同意,这确实是在gc'd环境和“手动完成所有操作”环境之间的很好的折衷。我很高兴在ObjC和LLVM中使用它(因此其他语言也可以利用它)。
davidmw 2012年

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系统最终将决定从堆栈和CPU寄存器开始查找它可以找到的所有内容。它将永远找不到A,B1,B2,B3,因此它将最终确定它们并将内存回收到其他对象中。

当您使用ARC或MRC并以A结尾时,它的引用计数为3(B1,B2和B3都引用它),而B1 / B2 / B3的引用计数都为1(A的NSArray持有一个引用每)。因此,即使没有对象可以使用它们,所有这些对象仍然保持活动状态。

常见的解决方案是确定其中一个引用需要弱(不影响引用计数)。这将对某些使用模式有效,例如,如果仅通过A引用B1 / B2 / B3。但是在其他模式下,它将失败。例如,如果您有时会按住B1,并期望通过父指针向上爬回并找到A。如果参考强度很弱,如果仅按住B1,A会(通常会蒸发)并吸收B2和B3用它。

有时候这不是问题,但是使用复杂的数据结构的一些非常有用且自然的方法很难与ARC / MRC一起使用。

因此,ARC针对GC所针对的同类问题。但是,ARC使用的使用模式集比GC多得多,因此,如果您使用GC语言(例如Java)并将ARC之类的东西移植到它上面,则某些程序将无法再工作(或至少会产生大量的废弃内存) ,并且可能会导致严重的交换问题或内存或交换空间不足。

您也可以说ARC在性能(或可预测性)方面具有更高的优先级,而GC在作为通用解决方案时具有更高的优先级。结果,GC与ARC相比,可预测的CPU /内存需求较低,并且性能(通常)较低,但可以处理任何使用模式。ARC在许多常见的使用模式下会更好地工作,但是在某些(有效!)使用模式下,它会崩溃并死掉。


“另一方面,ARC无法实现某些类型的数据结构”,我认为您的意思是没有提示就无法进行自动清理;显然,数据结构是。
史蒂芬·费舍尔

可以,但是在ARC下只能自动清除ObjC对象,因此“无自动清除” ==“无清除”。不过,我有空的时候会改口再回答。
条纹

@Stripes:ARC中的手动清理等效于手动中断周期,例如foo = nil
道格拉斯

“ [ARC]往往会具有更高的性能... ARC将性能放在首位”。令我惊讶的是,众所周知,引用计数比跟踪垃圾收集慢得多。flyingfrogblog.blogspot.co.uk/2011/01/…–
乔恩·哈罗普

2
从理论上讲,GC速度更快(每个引用计数操作必须与多处理器缓存保持一致,并且有很多)。实际上,唯一可用的ObjC GC系统要慢得多。对于GC系统,在用户可以感知的时间量内随机中断线程也是非常普遍的(有一些实时GC系统,但是它们并不常见,我认为它们具有“有趣的”约束)
条纹

4

魔法

但更具体地说,ARC的工作原理与代码完全相同(有一些细微差别)。ARC是一种编译时技术,与GC不同,后者是运行时,会对性能产生负面影响。ARC将为您跟踪对对象的引用,并根据正常规则综合保留/释放/自动释放方法。因此,ARC也可以在不再需要它们时立即释放它们,而不是纯粹出于约定而将它们扔到自动释放池中。

其他一些改进包括将弱引用归零,将块自动复制到堆,全面加速(自动释放池提高6倍!)。

有关所有这些工作原理的更详细讨论,请参见ARC上的LLVM文档


2
-1“ ARC是一种编译时技术,与GC不同,后者是运行时,会对性能产生负面影响”。在运行时引用计数增加,这是非常低效的。这就是为什么跟踪JVM和.NET等GC的速度如此之快的原因。
乔恩·哈罗普

1
@Jon:你有这个证明吗?根据我自己的阅读,似乎新的RC算法通常比M&S GC表现更好或更好。
xryl669 2014年

1
@ xryl669:GC手册(gchandbook.org)中有完整的解释。请注意,跟踪!= M&S。
乔恩·哈罗普

3

它与垃圾收集有很大的不同。您是否已看到警告,告知您可能正在不同的行上泄漏对象?这些语句甚至告诉您分配对象的位置。这已经向前迈进了一步,现在可以将retain/ release语句插入适当的位置,比大多数程序员要好,几乎100%的时间。有时候,您需要一些奇怪的保留对象实例来帮助解决问题。


0

Apple开发人员文档对此进行了很好的解释。阅读“ ARC的工作原理”

为了确保实例在仍然需要时不会消失,ARC跟踪当前引用每个类实例的属性,常量和变量的数量。只要仍存在至少一个对该实例的活动引用,ARC便不会取消分配该实例。

为了确保实例在仍然需要时不会消失,ARC跟踪当前引用每个类实例的属性,常量和变量的数量。只要仍存在至少一个对该实例的活动引用,ARC便不会取消分配该实例。

认识Diff。在垃圾回收和ARC之间:阅读


0

ARC是一种编译器功能,可提供对象的自动内存管理。

ARC 无需评估何时使用retain, releaseautorelease,而是可以评估对象的生存期要求并在编译时自动为您插入适当的内存管理调用。编译器还会为您生成适当的dealloc方法。

编译器retain/release在编译时插入必要的调用,但是这些调用在运行时执行,就像其他任何代码一样。

下图将使您更好地了解ARC的工作方式。

在此处输入图片说明

那些是iOS开发的新手,并且没有在Objective C上的工作经验。请参阅Apple的《高级内存管理编程指南》文档,以更好地了解内存管理。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.