垃圾收集(Garbage collection)是全自动地检测并移除不再使用的数据对象。垃圾收集器通常会在当可用内存减少到少于一个具体的阈值时运行。通常,它们以程序所知的可用的一组“基本”数据 —— 栈数据、全局变量、寄存器 —— 作为出发点。然后它们尝试去追踪通过这些数据连接到每一块数据。收集器找到的都是有用的数据;它没有找到的就是垃圾,可以被销毁并重新使用这些无用的数据。为了有效地管理内存,很多类型的垃圾收集器都需要知道数据结构内部指针的规划,所以,为了正确运行垃圾收集器,它们必须是语言本身的一部分。
- 复制(copying): 这些收集器将内存存储器分为两部分,只允许数据驻留在其中一部分上。它们定时地从“基本”的元素开始将数据从一部分复制到另一部分。内存新近被占用的部分现在成为活动的,另一部分上的所有内容都认为是垃圾。另外,当进行这项复制操作时,所有指针都必须被更新为指向每个内存条目的新位置。因此,为使用这种垃圾收集方法,垃圾收集器必须与编程语言集成在一起。
- 标记并清理(Mark and sweep):每一块数据都被加上一个标签。不定期的,所有标签都被设置为 0,收集器从“基本”的元素开始遍历数据。当它遇到内存时,就将标签标记为 1。最后没有被标记为 1 的所有内容都认为是垃圾,以后分配内存时会重新使用它们。
- 增量的(Incremental):增量垃圾收集器不需要遍历全部数据对象。因为在收集期间的突然等待,也因为与访问所有当前数据相关的缓存问题(所有内容都不得不被页入(page-in)),遍历所有内存会引发问题。增量收集器避免了这些问题。
- 保守的(Conservative):保守的垃圾收集器在管理内存时不需要知道与数据结构相关的任何信息。它们只查看所有数据类型,并假定它们 可以全部都是指针。所以,如果一个字节序列可以是一个指向一块被分配的内存的指针,那么收集器就将其标记为正在被引用。有时没有被引用的内存会被收集,这样会引发问题,例如,如果一个整数域中包含一个值,该值是已分配内存的地址。不过,这种情况极少发生,而且它只会浪费少量内存。保守的收集器的优势是,它们可以与任何编程语言相集成。
Hans Boehm 的保守垃圾收集器是可用的最流行的垃圾收集器之一,因为它是免费的,而且既是保守的又是增量的,可以使用 --enable-redirect-malloc
选项来构建它,并且可以将它用作系统分配程序的简易替代者(drop-in replacement)(用 malloc
/ free
代替它自己的 API)。实际上,如果这样做,您就可以使用与我们在示例分配程序中所使用的相同的 LD_PRELOAD
技巧,在系统上的几乎任何程序中启用垃圾收集。如果您怀疑某个程序正在泄漏内存,那么您可以使用这个垃圾收集器来控制进程。在早期,当 Mozilla 严重地泄漏内存时,很多人在其中使用了这项技术。这种垃圾收集器既可以在 Windows® 下运行,也可以在 UNIX 下运行。
垃圾收集的一些优点:
- 您永远不必担心内存的双重释放或者对象的生命周期。
- 使用某些收集器,您可以使用与常规分配相同的 API。
其缺点包括:
- 使用大部分收集器时,您都无法干涉何时释放内存。
- 在多数情况下,垃圾收集比其他形式的内存管理更慢。
- 垃圾收集错误引发的缺陷难于调试。
- 如果您忘记将不再使用的指针设置为 null,那么仍然会有内存泄漏。
一切都需要折衷:性能、易用、易于实现、支持线程的能力等,这里只列出了其中的一些。为了满足项目的要求,有很多内存管理模式可以供您使用。每种模式都有大量的实现,各有其优缺点。对很多项目来说,使用编程环境默认的技术就足够了,不过,当您的项目有特殊的需要时,了解可用的选择将会有帮助。下表对比了本文中涉及的内存管理策略。
策略 | 分配速度 | 回收速度 | 局部缓存 | 易用性 | 通用性 | 实时可用 | SMP 线程友好 |
定制分配程序 | 取决于实现 | 取决于实现 | 取决于实现 | 很难 | 无 | 取决于实现 | 取决于实现 |
简单分配程序 | 内存使用少时较快 | 很快 | 差 | 容易 | 高 | 否 | 否 |
GNU malloc | 中 | 快 | 中 | 容易 | 高 | 否 | 中 |
Hoard | 中 | 中 | 中 | 容易 | 高 | 否 | 是 |
引用计数 | N/A | N/A | 非常好 | 中 | 中 | 是(取决于 malloc 实现) | 取决于实现 |
池 | 中 | 非常快 | 极好 | 中 | 中 | 是(取决于 malloc 实现) | 取决于实现 |
垃圾收集 | 中(进行收集时慢) | 中 | 差 | 中 | 中 | 否 | 几乎不 |
增量垃圾收集 | 中 | 中 | 中 | 中 | 中 | 否 | 几乎不 |
增量保守垃圾收集 | 中 | 中 | 中 | 容易 | 高 | 否 | 几乎不 |