内存管理是编程过程当中的一个经典问题,早期在 C 语言时代,几乎都靠 malloc/free 手动管理内存。随着各个平台的发展,到如今被普遍采用的主要有两个方法:node
引用计数 (ARC,Automatic Reference Counting)数据库
GC (Garbage Collection)编程
由于 Java 的流行,GC 被普遍的认知。GC 简单的说是按期查找再也不使用的对象,释放对象占用的内存。性能
基于 GC,申请的对象不须要手动释放,只须要确认对象在再也不须要时,再也不被其余对象引用。操作系统
引用计数早期主要用于底层系统,好比文件系统的 inode 管理,后来 C++ 的 boost 库实现了一套完整的 ARC,目前流行的系统还有 Objective C 也是采用的 ARC。指针
ARC 的特色是,一个对象被引用时,引用计数增长 1,引用对象释放时,引用计数减小 1,若是引用计数为 0,释放对象。code
由于 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 | 定时扫描清理 | 慢 | 大 | 较小 |
开发一个项目时,采用什么样的平台,跟实际面对的场景有很大关系,没有一个技术是用来解决全部问题的。
通常来讲,对延迟和性能不敏感的系统,能够考虑带 GC 的平台,好比 Java、Go 等来开发,一般能够提升开发效率。
若是须要对系统的性能有良好的控制,或者平台的资源有限,ARC 是更好的选择。好比操做系统、数据库等选择 C 或者 C++。好比 iOS 的 Object C 就是采用 ARC,实际来看比使用 Java (GC) 的 Android 平台的表现要好太多。
可是 ARC 平台通常对开发者要求要更高。
最近出现的新语言 Rust
采用的是 ARC,可是 Rust 会在代码编译阶段对内存、指针的使用作严格的分析和检查,确保程序没有内存管理问题。至关于把 GC 的一部分工做移到编译阶段,这样程序的运行性能几乎没有损失,同时又大大减小内存管理相关的 Bug。
个人观察从 C++11
正式吸纳 boost
的 smart pointer
后,C++ 在内存管理方面比以前有极大的提高,若是严格的按照 smart pointer 的规范,一样能够减小内存管理的风险。Rust 就有点像一个严格的 C++11 编译系统。
支持 GC 的平台里面有一个特殊的,就是 Erlang
。Erlang 的 GC 是进程级别的(Erlang 的轻量级进程),意味着 GC 发生时,只暂停当前进程,其余进程不受影响。另外,Erlang 程序每每会运行海量的进程,至关于把 GC 分散开了,因此 Erlang 的 GC 通常不会产生明显的延迟。
了解这些细节,在面对具体问题时,能帮你作出正确的选择。
欢迎来微博留下您的意见:http://weibo.com/2255454164/E...
做者:张虎
weibo: @Tiger_张虎, 云巴 (yunba.io) 创始人,yunba.io 云端实时消息服务。 JPush 创始人,原CTO。 Oracle VM 创始团队成员。