209

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

另外,如果 ARC 在不影响性能的情况下完成了 GC 的工作,那么 Java 为什么要使用 GC?为什么它也不使用ARC?

4

6 回答 6

244

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

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

自动引用计数(ARC) 是下一个合乎逻辑的步骤。如果编译器可以识别您应该在哪里保留和释放对象,为什么不让它为您插入该代码呢?刚性、重复性的任务是编译器和他们的兄弟们擅长的。人类会忘记事情并犯错误,但计算机的一致性要高得多。

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

与手动内存管理和垃圾收集相比,ARC 通过减少编写保留/释放代码的需要,为您提供两全其美的体验,同时又没有在垃圾收集环境中看到的暂停和锯齿状内存配置文件。关于垃圾收集的唯一优势是它处理保留周期的能力以及原子属性分配成本低廉的事实(如这里所讨论的)。我知道我正在用 ARC 实现替换所有现有的 Mac GC 代码。

至于这是否可以扩展到其他语言,它似乎围绕着 Objective-C 中的引用计数系统。将其应用于 Java 或其他语言可能很困难,但我对低级编译器细节的了解还不够,无法在那里做出明确的陈述。鉴于 Apple 是在 LLVM 中推动这一努力的人,除非另一方为此投入大量资源,否则 Objective-C 将首先出现。

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

于 2011-06-20T22:43:31.627 回答
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 的 CPU/内存需求可预测性较低,性能(通常)低于 ARC,但可以处理任何使用模式。ARC 对于许多常见的使用模式会更好地工作,但对于一些(有效的!)使用模式,它会崩溃并死掉。

于 2011-11-22T08:15:38.713 回答
4

魔法

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

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

在 ARC 上的LLVM Docs中可以找到有关所有这些工作原理的更详细讨论。

于 2011-06-17T13:08:25.423 回答
3

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

于 2011-06-17T12:49:52.207 回答
0

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

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

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

下图将使您更好地了解 ARC 的工作原理。

在此处输入图像描述

那些刚接触 iOS 开发且没有 Objective C 工作经验的人。请参阅 Apple 的高级内存管理编程指南文档以更好地了解内存管理。

于 2019-05-03T18:36:16.317 回答
0

Apple 开发人员文档很好地解释了这一点。阅读“ARC如何工作”

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

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

要知道差异。垃圾收集和 ARC 之间:阅读此内容

于 2017-03-31T10:27:20.133 回答