欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

引用计数 vs. GC  

程序员文章站 2022-03-08 10:03:50
...

内存管理问题

内存管理是编程过程中的一个经典问题,早期在 C 语言时代,几乎都靠 malloc/free 手动管理内存。随着各个平台的发展,到现在被广泛采用的主要有两个方法:

  • 引用计数 (ARC,Automatic Reference Counting)
  • GC (Garbage Collection)

管理方法 ARC/GC

因为 Java 的流行,GC 被广泛的认知。GC 简单的说是定期查找不再使用的对象,释放对象占用的内存。

基于 GC,申请的对象不需要手动释放,只需要确认对象在不再需要时,不再被其他对象引用。

引用计数早期主要用于底层系统,比如文件系统的 inode 管理,后来 C++ 的 boost 库实现了一套完整的 ARC,目前流行的系统还有 Objective C 也是采用的 ARC。

ARC 的特点是,一个对象被引用时,引用计数增加 1,引用对象释放时,引用计数减少 1,如果引用计数为 0,释放对象。

比较

因为 ARC 和 GC 的不同策略,对编程几个方面的影响如下。

性能

GC 需要一套额外的系统跟踪分配的内存,分析哪些内存需要释放,相对来说就需要更多的计算。这也是为什么对性能敏感的场景不采用 GC 的原因,比如,高性能的服务端程序,资源有限的嵌入式设备(iOS 就没有采用 GC)。

ARC 由开发者自己来管理资源在什么时候释放,不需要额外的资源,所以性能没有损失。

延迟

GC 回收内存时,需要完全暂停当前程序,这会给程序带来难以预测的一个延迟期。如果需要回收的资源很多,这个延迟可能会非常大。

ARC 在资源引用为 0 时立即释放,没有不可预测的延迟。

编程难度

不难看出,GC 在性能、延迟等方面有明显的缺点,为什么 GC 还会被广泛采用呢?

GC 带来的最大好处是不需要开发者手动管理内存分配,这大大降低了编程难度,同时可以大幅减少跟内存管理相关的 Bug:

  • 悬空指针。指针指向的内存被其他代码释放
  • 重复释放内存
  • 内存泄漏。申请的内存没释放

不过使用 GC 并不代表可以完全不用理解内存管理,如果对象的引用关系跟想象的不一致,GC 也会有内存泄漏的问题

我们之前理解的内存泄漏 是指一个分配的内存没有被释放造成的。而 GC 平台下的内存泄漏是指对象有引用而开发者不知道,比如:

ObjectA -> ObjectB

ObjectB 使用完后,我们没有及时把 ObjectA 引用 ObjectB 的指针设置为 NULL,这时, ObjectB 不会被 GC 回收。

  • 对比表格
时机 性能 延迟 编程难度
ARC 引用计数为 0 马上回收 较大
GC 定时扫描清理 较小

怎么选择 ARC or GC

开发一个项目时,采用什么样的平台,跟实际面对的场景有很大关系,没有一个技术是用来解决所有问题的。

一般来说,对延迟和性能不敏感的系统,可以考虑带 GC 的平台,比如 Java、Go 等来开发,通常可以提高开发效率。

如果需要对系统的性能有良好的控制,或者平台的资源有限,ARC 是更好的选择。比如操作系统、数据库等选择 C 或者 C++。比如 iOS 的 Object C 就是采用 ARC,实际来看比使用 Java (GC) 的 Android 平台的表现要好太多。

但是 ARC 平台一般对开发者要求要更高。

最近出现的新语言Rust采用的是 ARC,但是 Rust 会在代码编译阶段对内存、指针的使用做严格的分析和检查,确保程序没有内存管理问题。相当于把 GC 的一部分工作移到编译阶段,这样程序的运行性能几乎没有损失,同时又大大减少内存管理相关的 Bug。

我的观察从C++11 正式吸纳boostsmart pointer后,C++ 在内存管理方面比之前有极大的提升,如果严格的按照 smart pointer 的规范,同样可以减少内存管理的风险。Rust 就有点像一个严格的 C++11 编译系统。

支持 GC 的平台里面有一个特殊的,就是 Erlang。Erlang 的 GC 是进程级别的(Erlang 的轻量级进程),意味着 GC 发生时,只暂停当前进程,其他进程不受影响。另外,Erlang 程序往往会运行海量的进程,相当于把 GC 分散开了,所以 Erlang 的 GC 一般不会产生明显的延迟。

了解这些细节,在面对具体问题时,能帮你做出正确的选择。

欢迎来微博留下您的意见:http://weibo.com/2255454164/E48lBE9pU

作者:张虎

weibo: @Tiger_张虎, 云巴 (yunba.io) 创始人,yunba.io 云端实时消息服务。 JPush 创始人,原CTO。 Oracle VM 创始团队成员。

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>