关于C#:新的自动参考计数机制是如何工作的?

How does the new automatic reference counting mechanism work?

有人能给我简单解释一下ARC是如何工作的吗?我知道这和垃圾收集不同,但我只是想知道它是如何工作的。

此外,如果ARC在不妨碍性能的情况下运行GC,那么为什么Java使用GC?为什么它不使用弧线呢?


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

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

自动参考计数(ARC)是下一个逻辑步骤。如果编译器能够识别应该在哪里保留和释放对象,为什么不让它为您插入代码呢?严格的、重复的任务是编译器和它们的兄弟们所擅长的。人类忘记了事情,犯了错误,但计算机更为一致。

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

与手动内存管理和垃圾收集相比,ARC通过减少编写保留/释放代码的需要,而不是在垃圾收集环境中看到停止和锯齿形内存配置文件,为您提供了两个方面的最佳选择。垃圾收集相对于这一点的唯一优势是它处理保留周期的能力,以及原子属性分配便宜的事实(如本文所讨论的)。我知道我正在用ARC实现替换我现有的所有macgc代码。

至于这是否可以扩展到其他语言,它似乎与Objtovi-C中的引用计数系统相适应。很难将其应用于Java或其他语言,但我对低级编译器细节不太了解,无法在那里作出明确的声明。考虑到苹果是LLVM中推动这一努力的一方,除非另一方为此投入大量资源,否则Objective-C将首先出现。

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


ARC只是在编译器确定何时调用RETAIN/RELEASE时播放旧的RETAIN/RELEASE(MRC)。与GC系统相比,它具有更高的性能、更低的峰值内存使用率和更可预测的性能。

另一方面,ARC(或MRC)不可能使用某些类型的数据结构,而GC可以处理它们。

例如,如果您有一个名为node的类,并且node有一个nsarray的子类,并且有一个对它的父类的引用,这个引用"只与gc一起工作"。使用ARC(以及手动参考计数),您会遇到问题。任何给定节点都将从其子节点和父节点引用。

像:

1
2
A -> [B1, B2, B3]
B1 -> A, B2 -> A, B3 -> 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工作得更好,但对于少数(有效!)使用模式,它将倒下并死亡。


魔术

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

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

关于所有这些如何工作的更详细的讨论可以在ARC上的LLVM文档中找到。


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


苹果开发人员的文档解释得很好。阅读"ARC如何工作"

To make sure that instances don’t disappear while they are still needed, ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists.

To make sure that instances don’t disappear while they are still needed, ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists.

要了解垃圾收集和ARC之间的区别,请阅读以下内容