问题
自动引用计数(ARC) 和 垃圾回收(GC)有什么区别?
解答
在讨论自动引用计数(ARC) 和 垃圾回收(GC)关系之前,我们先讨论下对象的生命周期管理。
对象生命周期管理作为编程语言的一个特性,用来跟踪对象在内存中被销毁和释放之前存在多久。
跟踪内存可以及时地释放对象,在内存作为稀缺资源的设备上特别重要,比如移动设备。如果太多过期的对象长期存在内存中,应用乃至整在操作系统在性能上都会被拖累,从而不能进行其他操作。
在引用现代的对象生命周期管理之前,开发者必须要手动跟踪自己创建的对象,从而确保在使用结束后手动释放所有对象。这样不仅要很多的类似的业务无关代码,而且这类代码维护性较难。
例如,在开发语言Delphi中,所有创建的对象必须要明确地通过调用方法 Free 释放,你会在很多场景中看到很多try/finally模块仅仅为了释放本地对象。如果对象从其他方法中返回,或是一个类的属性,这样很难甄别对象的所有权。
在Objective-C引进ARC之前,为了跟踪对象的所有权需要手动地调用retain 和 release。
无论是垃圾回收,还是自动引用计数,都是为了减少开发者的负担,这两项技术对于开发者来说,在语言层面是透明的。开发者不用考虑对象的所有权,也不用在对象使用后手动释放, 也就是说程序员在日常工作中再也不用考虑对象生命周期管理的问题。
对象生命周期管理主要是跟踪对象是否依然被其他对象使用,简单来说,只要有任何代码持有对象的引用就表明该对象仍然有用。
这里主要考虑三个场景:
- 一个对象存储在另一个对象的字段或属性里。
- 本地对象在当前方法体内创建,也包括定义在方法外部,却在匿名函数里使用。
- 对象作为参数在调用方法时使用。
在上面的三个场景中,如果对象依然被需要,支持垃圾回收或自动引用计数机制的编译器会保存而不释放对象,例如,如果存储一个对象在一个字段或属性内,字段内会有一个有效的引用直到该字段被其他引用覆盖,或该对象释放自身。还有如果在方法体内定义本地变量,编译器会保证在作用域范围内变量一直有效。
当然上述三个场景也可能组合出现。若同一个对象存储在一个字段和一个本地变量内,只有在两者都释放对象引用的时候,才能认为该对象可以被释放。
简单来说, 虽然垃圾回收或自动引用计数的实现方式差别很大, 但支持的编译器都可以让你放心地访问任何你可以访问范围的对象,而不用担心被释放。且会自动释放那些你不再使用的对象,
垃圾回收
垃圾回收技术主要使用在.NET 和 Java 平台,在两个平台(Java Runtime 和 .NET Common Language Runtime)的上都有一套机制在后台检测不用的对象和对象图谱。
垃圾回收运用到间隔周期不是固定的,有可能是系统检测到内存占用较低,也可能是上次运行过了一段固定时间等,这样就造成了那些不再使用的对象被释放的时间难以确定。
-
优势
垃圾收集可以清理所有不被使用的对象图谱,包括循环引用。
垃圾回收运行在后台,可以作为定期应用程序流程运行。 -
劣势
对象释放的时间范围是难以确定的。
当垃圾回收运行时,可能导致系统资源紧张,需暂停一些优先级较低的线程。
自动引用计数
自动引用计数主要体现在Cocoa平台,不像垃圾回收是在后台检测并回收不用的对象,编译器会自动注入可执行代码来跟踪引用计数,并在需要的时候释放对象。如果你尝试反编译支持ARC的可执行文件,看起来就像开发者花很多时间写代码处理对象生命周期一样, 其实这些繁重的工作都是编辑器完成的。
-
优势
实时地,精确地释放那些变成没用的对象。
没有后台处理,在低功耗的系统上更有效率,如手机设备。 -
劣势
不能处理循环引用。
在Swift在如何处理循环引用可参看Swift闭包和函数
推荐阅读
经典面试100题 - 持续更新中
网友评论