Objective-C自动引用计数与垃圾回收有何区别?

62
使用Xcode 4.2引入的新自动引用计数(ARC),我们不再需要在Objective-C中手动管理保留/释放。
这似乎类似于在Mac上使用Objective-C和其他语言中执行的垃圾回收。 ARC与垃圾回收有何不同?

4个回答

73

如我在这里所述,ARC可以同时提供手动内存管理和追踪垃圾回收的优点。它大部分减少了开发人员跟踪Objective-C对象上的手动retain、release和autorelease的需求,同时避免了垃圾回收进程的需要,后者可能会消耗移动设备上有限的资源并导致应用程序偶尔出现停滞。

ARC在编译时插入了所需的保留和释放以进行引用计数,应用了所有Objective-C开发人员多年来必须使用的规则,因此开发人员无需自己管理这些操作。由于保留和释放是在编译时插入的,因此不需要收集器进程不断地扫描内存并删除未被引用的对象。

追踪垃圾回收相对于ARC的一个微小优势是,ARC无法处理保留循环(retain cycles),而追踪垃圾回收可以检测到这些循环。

关于这个话题的一篇很好的文章来自苹果Objective-C邮件列表中的这个帖子,其中Chris Lattner说:

GC相对于ARC的主要优点是它可以回收保留循环。其次,由于“保留”赋值只是一个简单的存储,因此它们是“原子”的。ARC相对于libauto GC具有几个巨大的优势:

  1. 它对对象进行了确定性(当对象的最后一个strong引用消失时)回收,而GC在“以后某个时间”释放对象。这定义了GC应用程序中可能存在但未暴露的一类微妙错误。
  2. 由于对象更早地释放,所以ARC的高水位线通常比GC低得多。
  3. libauto提供了一种脆弱的编程模型,你必须小心,以免丢失写入障碍等。
  4. 并非所有系统框架都是GC干净的,并且随着它们的演变,这些框架偶尔会出现回归。
  5. ARC不会遭受虚假根节点。 libauto保守地扫描堆栈,这意味着看起来像指针的整数可以成为对象图的根节点。
  6. ARC没有任何会启动并停止你的应用程序,导致UI卡顿的东西。 就GC实现而言,libauto相当先进,因为它不会立即停止每个线程,但通常仍然会停止所有UI线程。
  7. 我目前正在将我的手动内存管理项目以及使用Objective-C垃圾回收的项目迁移到ARC。在几个Mac应用程序中使用垃圾回收后,我看到将这些项目迁移到ARC中有一些显着优势。


3
ARC 相比于 libauto 的另一个优点是它不会减少虚拟地址空间。请参见 https://dev59.com/2W435IYBdhLWcg3w3EEi#5522746。 - Nikolai Ruhe
6
我想强调的是,Chris Lattner所说的是关于libauto而不是普遍的追踪垃圾回收。我还要指出,ARC是一种垃圾回收形式,因此讨论“ARC vs GC”是没有意义的。你真正想表达的是“ARC vs 追踪GC”。 - J D
3
@Asik - 或许我的措辞不够准确。我已经修改了上述内容,以说明我的意思,即用于引用计数的保留和释放是在编译时插入的。当然,引用计数本身是在运行时发生的,但重点是这避免了追踪垃圾回收所需的收集器进程。此问题的背景是关于Objective-C(libauto)垃圾回收的,因此我认为阅读此文章的人会理解“垃圾回收”是指使用收集器进程的追踪垃圾回收。 - Brad Larson
2
@Asik - 就卡顿而言,ARC在对象和对象图被释放的位置上是确定性的。这与对象在不再需要时释放的事实相结合,意味着实际上你会看到比收集器进程在特定间隔扫描对象图时更少的卡顿。此外,它的确定性使得很容易识别造成停顿或暂停的释放点,并重新设计适当的操作以避免或显著减少这种情况。 - Brad Larson
1
你是说循环收集只是一个“轻微”的优势吗?尝试在你的代码中更复杂地使用块(ObjC闭包):-) 另一个区别是跟踪的CPU效率,这不需要在你的代码中到处添加原子引用计数操作。 - TaylanKammer
显示剩余2条评论

22

ARC 依靠在编译时“引用”对象,使其在低功率环境下(移动设备)高效。

GC 依靠在运行时“可达”对象,使其在多线程环境下高效。

操作

ARC将代码注入可执行文件中,以便在引用计数未使用的对象上“自动”执行。

GC在运行时工作,它会检测未使用的对象图(并消除保留循环),并在不确定的时间间隔内删除它们。

自动引用计数的优点

  • 实时、确定性地销毁对象,因为它们变成未使用状态。
  • 没有后台处理。

垃圾回收的优点

  • GC可以清理整个对象图,包括保留循环。
  • GC在后台进行,因此在正常应用程序流程中不需要进行太多的内存管理工作。

自动引用计数的缺点

  • ARC无法自动处理保留循环。

垃圾回收的缺点

  • 由于GC在后台运行,因此对象释放的确切时间框架是不确定的。
  • 当GC发生时,应用程序中的其他线程可能会被暂时挂起。

9

ARC和垃圾回收有什么不同?

ARC是一种垃圾回收形式。

你可能想问的是“ARC和追踪垃圾回收(如JVM和.NET)之间的区别是什么?”主要区别在于ARC较慢且会泄漏循环。这就是为什么JVM和.NET都使用追踪垃圾收集器的原因。欲了解更多信息,请阅读引用计数和追踪垃圾回收的比较


我知道引用计数可能会泄漏循环,但它为什么会更慢呢?一旦引用计数达到零,资源就可以被释放。在跟踪垃圾收集中,在释放资源之前需要多长时间是无法确定的,这里有什么不正确的地方吗? - http8086
1
@hylepo:这是正确的,经常被引用,但是很容易误导人。“一旦RC达到零”,但是计数何时达到零?减量几乎总是推迟到作用域的末尾,因此对象无论是否需要都会保持到作用域的末尾。因此,与普遍观点相反,RC不会在最早的时间收集。“为什么它会更慢?”参考计数很慢,因为它产生了许多通常是非本地和因此缓存不友好的计数器增量和减量。在多线程上下文中更糟糕,其中inc/dec必须是原子的。 - J D
@hylepo:这些都是已解决的问题,但所有的解决方案都会牺牲你所提到的好处。例如,你可以推迟减量以分摊成本,但 RC 就像跟踪 GC 一样不可预测。此外,请注意,RC 仅适用于单线程程序。一旦你有了多线程,你就可能会遇到减量竞争的情况,无法预测哪个线程将被阻塞进行清理。 - J D

-8

简短而明了的答案如下:

Java的GC是运行时进行垃圾回收,而ARC是编译时进行。

GC在运行时引用对象并检查其依赖关系。 而ARC在编译时附加释放、保留和自动释放调用。


46
这显然是不正确的。ARC在运行时会增加引用计数,只有当引用计数为零时才会释放。此外,ARC是一种垃圾回收形式,所以你实际上是指“跟踪垃圾回收与引用计数垃圾回收”。 - J D
27
哇,这是流行神话如何被维持的一个例子;令人疯狂的是,这得到了投票支持和认可。正如Jon Harrop所说,这个答案是完全错误的:引用计数是一种运行时机制。 - Asik
4
其他人已经指出,这个答案是完全错误的。垃圾回收可能是存在最荒诞的神话和误解之一。有些伪智者散布虚假的谎言也毫不帮助。http://taylanub.github.io/webapps-js-gc/ - TaylanKammer
9
这个答案是正确的:在编译时会添加释放和保留调用。当然,实际的增量/减量发生在运行时,但是这些调用会在运行时插入。 - Jimothy
11
自动引用计数(ARC)是一种编译时的特性。具体来说,ARC会在编译期使用LLVM自动注入retain、release和autorelease等Objective-C运行时的等效代码。如果没有ARC或GC,您需要手动添加retain和release调用,这就是手动引用计数(MRC)。在后者的情况下,人们会在开发时将这些调用添加到源代码中,而不是在运行时或编译时。如何添加这些调用的策略不应与引用计数本身的策略混淆,后者确实发生在运行时。 - Scott Ahten
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接