ChatGPT解决这个技术问题 Extra ChatGPT

新的自动引用计数机制是如何工作的?

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

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

这将告诉您所有相关信息:http://clang.llvm.org/docs/AutomaticReferenceCounting.html 它在 Xcode 和 iOS 5 中的实现方式受保密协议约束。
@mbehan 这是一个糟糕的建议。我不想登录,甚至不想拥有 iOS 开发中心的帐户,但我仍然有兴趣了解 ARC。
ARC 并没有做 GC 所做的所有事情,它要求您明确地使用强引用语义和弱引用语义,如果您没有正确处理这些语义,则会泄漏内存。以我的经验,当你在 Objective-C 中使用块时,这起初是很棘手的,即使在你了解了这些技巧之后,你也会留下一些关于块的许多用法的烦人 (IMO) 样板代码。忘记强/弱引用更方便。此外,GC 可以比 ARC wrt 执行得更好一些。 CPU,但需要更多内存。当您拥有大量内存时,它可能比显式内存管理更快。
@TaylanUB:“需要更多内存”。很多人都这么说,但我很难相信。
@JonHarrop:说实话,目前我什至不记得我为什么这么说。 :-) 与此同时,我意识到有这么多不同的 GC 策略,以至于这些笼统的陈述可能都毫无价值。让我从他的记忆分配神话和半真半假中背诵 Hans Boehm:“为什么这个地区如此容易出现可疑的民间智慧?”

C
Community

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

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

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

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

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

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

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


强调我的:这并不能完全让您不必担心内存管理
ARC真的是一项创新吗?从您的回答中,我得出结论,ARC 是一个新概念,它是第一次在 Objective-C 中使用(如果我错了,请纠正我)。老实说,我不是 Objective-C 开发人员,对 ARC 了解不多,但是 Boost Shared Pointers(参见 boost.org)不完全一样吗?如果不是,有什么区别?
@DMM - 这是一个编译器级别的过程,而不是依赖于重载的运算符(如 Boost 那样),它将它扩展到整个语言。除此之外,这使得将手动引用计数的应用程序转换为 ARC 变得很容易。 Boost 也可能以不同于 ARC 的方式处理局部变量,ARC 知道局部变量不再被使用的时刻,并且可以在那时释放。我相信使用 Boost,您仍然需要以某种方式指定您已完成该变量。
为了回答“是不是新的”问题,十多年来,Delphi 对字符串、数组和接口(用于 COM 支持)进行了自动引用计数。我同意这确实是 gc'd 环境和“手动完成”环境之间的一个很好的折衷方案。我很高兴它在 ObjC 和 LLVM 中(因此其他语言也可以利用它)。
@theDmi:“ARC 真的是一项创新吗?”。自动引用计数发明于 1960 年,并已在 Python 和 Mathematica 等多种语言中使用。它没有在 JVM 或 CLR 中使用,因为它非常慢并且会泄漏周期。
E
Erik Kaplun

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 对于许多常见的使用模式会更好地工作,但对于一些(有效的!)使用模式,它会崩溃并死掉。


“另一方面,ARC无法使用某些类型的数据结构”我认为您的意思是没有提示就无法进行自动清理;显然,数据结构是。
当然,但在 ARC 下只能自动清理 ObjC 对象,因此“无自动清理”==“无清理”。不过,当我有更多时间时,我会改写然后回答。
@Stripes:ARC 中手动清理的等价物是手动中断循环,例如 foo = nil
“[ARC] 将倾向于具有更高的性能......ARC 更重视性能”。当众所周知引用计数比跟踪垃圾收集慢得多时,我很惊讶。 flyingfrogblog.blogspot.co.uk/2011/01/…
理论上 GC 更快(每个引用计数操作必须是多处理器缓存一致的,并且有很多)。实际上,ObjC 唯一可用的 GC 系统要慢得多。 GC 系统在用户可感知的时间内随机暂停线程也是非常常见的(有一些实时 GC 系统,但它们并不常见,我认为它们具有“有趣”的约束)
J
Joshua Weinberg

魔法

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

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

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


-1 “ARC 是一种编译时技术,与运行时的 GC 不同,会对您的性能产生负面影响”。引用计数在运行时增加,这是非常低效的。这就是为什么像 JVM 和 .NET 这样的 GC 跟踪速度要快得多。
@Jon:你有证据吗?从我自己的阅读来看,似乎新的 RC 算法的性能通常与 M&S GC 一样好或更好。
@xryl669:GC 手册 (gchandbook.org) 中有完整的解释。请注意,跟踪!= M&S。
F
FreeAsInBeer

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


C
Community

Apple 开发人员文档很好地解释了这一点。阅读"How ARC Works"

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

要知道差异。在垃圾收集和 ARC 之间:阅读 this


2
2 revs

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

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

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

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

https://i.stack.imgur.com/0Xu0Z.png

那些不熟悉 iOS 开发且没有 Objective C 工作经验的人。请参阅 Apple 的 Advanced Memory Management Programming Guide 文档以更好地了解内存管理。