ObjectiveC的内存管理1内存管理概述

4次阅读

共计 1852 个字符,预计需要花费 5 分钟才能阅读完成。

概述

应用程序开发中,内存管理是个重要的话题。
简单而言,语言层面的内存管理基本有三类:

1. 纯粹的手动管理

如 C 和曾经的 C ++。

char *some_string = malloc(BUFFER_SIZE);
// do something
free(some_string);

这个简单的例子里用完就释放还好,但是有时候这个 some_string 被传来传去不知道飞哪儿去了,就比较尴尬。
纯手动管理的代价是程序员的心智负担比较重。
即使后来 C ++ 程序员们抽象出 RAII 这样的实践规范,一定程度上降低了管理的复杂度,但是相对来说成本还是略高。
随着语言的发展,已经很少有语言只依赖手动管理内存了。

2. 基于某些机制实现半自动管理

这里的某些机制其实通常就是引用计数。毕竟这是最简单的内存管理辅助手段。

引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。

引用计数大家都了解,不多说,单纯的自动使用引用计数问题在于无法解决循环引用的问题。很多语言选择让程序员付出一点劳动来解决这个问题。
早年,Objc 选择的是退一步,完全让程序员来管理引用计数的加减,称为 MRC,显然管理成本偏高。后来推出了 ARC,提供了更健全的机制,程序员只要标识出对象间的引用关系是强引用还是弱引用就可以了,大大降低了程序员的负担。
虽然走这个路子的语言不算多,但除了 Objc 之外还是有好几个的。
C++ 的智能指针跟 Objc 的 ARC 就比较相似。而 Rust 的所有权模型本质上也是类似的。

3. 自动垃圾回收

通过 GC 自动管理内存大概是现在的主流了。对程序员来讲实在是太舒适了,开发时几乎不用考虑内存管理的问题。Java、JavaScript、Python、go 等一大票语言都是走的这条路。
GC 是基于可达性分析算法的,即,从根节点(全局变量、局部变量等等)出发,遍历引用到的对象,所有没遍历到的对象就可以释放了。
当然从原理到实际应用中间差了十万八千里。朴素的 GC 会经常造成 Stop The World。一旦 Stop-the-world 发生,除了 GC 所需的线程外,其他线程都将停止工作,中断了的线程直到 GC 任务结束才继续它们的任务。
于是很多 GC 算法被发明出来用于优化、减少。常见的 CMS、G1 回收算法都极大地减少了 STW 的时间,但仍然不能完全避免。
R 大在 Java 大内存应用(10G 以上)会不会出现严重的停顿?中提到 Zing JVM 采用的 C4 算法是可以完全避免 STW 的,不过看起来为了避免 STW,C4 算法会吃掉更多的内存,程序吞吐量会受到影响。

小结

总结一下吧,纯手动管理基本上已被淘汰,ARC(暂且把方法二这类都称为 ARC 吧) 和 GC 对比之下,
对开发者,ARC 需要程序员付出一定的代价进行管理,GC 则基本上完全解放双手;
性能上,GC 通常会造成 STW 现象,对响应时间比较敏感的程序,比如高频交易系统,是很难接受的,而 ARC 不会对性能造成明显影响。

几点有趣的事情

1. 总体性能

总体性能上,只要不是内存跑得特别满,ARC 的总体代价是高于 GC 的。其实想想就知道了,GC 只关注两次回收间的变化,而 ARC 要对每一次引用的改变进行计数,总体性能上比 GC 差是很正常的,但由于 ARC 的耗时是均匀分布在运行时间里的,通常我们不用很关注。关于这个问题可以参考这篇论文。

2. cpython 的方案

另外比较特别的是,Python 的默认解释器 CPython 中应用了引用计数与垃圾回收相结合的手法,没有循环引用的对象会被引用计数回收,剩下的交给 GC 处理,大大降低了 GC 的压力。感觉很有意思。

3. ARC 的性能

在类 ARC 方案上,C++ 提供的能力是比较全面的。这可能跟 c ++ 常用于一些性能比较苛刻的场景有关。
出于性能原因,使用 c ++ 智能指针时有如下指导思想:

  1. 对象的所有权不重要时,用裸指针
  2. 对象的所有权唯一时,用 unique_ptr,能用 unique_ptr 就不要用 shared_ptr。
  3. 要处理复杂情况时,可以使用 shared_ptr,但需要注意不要滥用。当引用关系不影响所有权时,用 weak_ptr。

Rust 也有类似的能力。
而 python 和 Objective- C 就没有这么多讲究,所有的引用计数其实都是 shared_ptr。Objc 在 iOS 上这么多年,而后来的 swift 也传承了 ARC,基本上可以认为,移动端应用从小到大都不差这么一丢丢性能。

以此推论,绝大部分 c ++ 应用也完全没必要关注这几个指针间的差异,操起 shared_ptr 就是干。

正文完
 0