Java 中有哪些垃圾回收算法?

八股文一网打尽,更多面试题请看程序员面试刷题神器 - 面试鸭open in new window

回答重点

Java 中的垃圾回收算法主要有以下几种:

标记-清除算法(Mark-Sweep)

  • 工作原理:首先遍历堆中的对象,标记出所有的存活对象,接着清除未标记的对象。
  • 优点:实现简单,能够处理堆中的所有对象。
  • 缺点:标记和清除的过程会产生内存碎片,影响后续内存分配的效率。

标记-整理算法(Mark-Compact)

  • 工作原理:首先标记出所有存活的对象,然后将存活的对象整理到一边,最后清除未标记的对象。
  • 优点:避免了内存碎片问题。
  • 缺点:整理阶段需要移动对象,会导致额外的开销。

复制算法(Copying)

  • 工作原理:将内存分成两部分,每次只使用其中一半,垃圾回收时将存活的对象从一半复制到另一半,清除原区域的所有对象(朴素的复制算法是这样的,实际使用会分为两个 survivor 和一个 eden 区)。
  • 优点:无需处理内存碎片,分配效率高。
  • 缺点:需要双倍的内存空间,浪费了一半的空间。

扩展知识

标记-清除详细分析

分为两个阶段:

标记阶段:tracing 阶段,从根(栈、寄存器、全局变量等)开始遍历对象图,标记所遇到的每个对象。

清除阶段:扫描堆中的对象,将为标记的对象作为垃圾回收。

基本上就是下图所示这个过程:

image.png

清除不会移动和整理内存空间,一般都是通过空闲链表(双向链表)来标记哪一块内存空闲可用,因此会导致一个情况:空间碎片

这会使得明明总的内存是够的,但是申请内存就是不足。

image.png

而且在申请内存的时候也有点麻烦,需要遍历链表查找合适的内存块,会比较耗时。

所以会有多个空闲链表的实现,也就是根据内存分块大小组成不同的链表,比如分为大分块链表和小分块链表,这样根据申请的内存分块大小遍历不同的链表,加快申请的效率。

image.png

当然还可以分更多个链表。

还有标记,标记的话一般我们会觉得应该是标记在对象身上,比如标记位放在对象头中,但是这对写时复制不兼容。

等于每一次 GC 都需要修改对象,假设是 fork 出来的,其实是共享一块内存,那修改必然导致复制。

所以有一种位图标记法,其实就是将堆的内存某个块用一个位来标记。就像我们的内存是一页一页的,堆中的内存可以分成一块一块,而对象就是在一块,或者多块内存上。

根据对象所在的地址和堆的起始地址就可以算出对象是在第几块上,然后用一个位图中的第几位在置为 1 ,表明这块地址上的对象被标记了。

image.png

而且用位图表格法不仅可以利用写时复制,清除也更加高效,如果标记在对象头上,那么需要遍历整个堆来扫描对象,现在有了位图,可以快速遍历清除对象。

但是不论是标记对象头还是利用位图,标记-清除的碎片问题还是处理不了。

因此就引出了标记-复制和标记-整理。

标记-复制详细分析

首先这个算法会把堆分为两块,一块是 From、一块是 To。

对象只会在 From 上生成,发生 GC 之后会找到所有存活对象,然后将其复制到 To 区,之后整体回收 From 区。

再将 To 区和 From 区身份对调,即 To 变成 From , From 变成 To,我再用图来解释一波。

image.png

可以看到内存的分配是紧凑的,不会有内存碎片的产生

不需要空闲链表的存在,直接移动指针分配内存,效率很高。

对 CPU缓存亲和性高,因为从根开始遍历一个节点,是深度优先遍历,把关联的对象都找到,然后内存分配在相近的地方。

这样根据局部性原理,一个对象被加载了那它所引用的对象也同时被加载,因此访问缓存直接命中。、

当然它也是有缺点的,因为对象的分配只能在 From 区,而 From 区只有堆一半大小,因此内存的利用率是 50%。

其次如果存活的对象很多,那么复制的压力还是很大的,会比较慢。

然后由于需要移动对象,因此不适用于上文提到的保守式 GC。

当然我上面描述的是深度优先就是递归调用,有栈溢出风险,还有一种 Cheney 的 GC 复制算法,是采用迭代的广度优先遍历,具体不做分析了,有兴趣自行搜索。

标记-整理详细分析

标记-整理其实和标记-复制差不多,区别在于复制算法是分为两个区来回复制,而整理不分区,直接整理。

image.png

算法思路还是很清晰的,将存活的对象往边界整理,也没有内存碎片,也不需要复制算法那样腾出一半的空间,所以内存利用率也高。

缺点就是需要对堆进行多次搜索,毕竟是在一个空间内又标记,又移动的,所以整体而言花费的时间较多,而且如果堆很大的情况,那么消耗的时间将更加突出。

至此相信你对标记-清除、标记-复制和标记-整理都清晰了,让我们再回到刚才提到的分代收集。

新生代的复制算法

八股文一网打尽,更多面试题请看程序员面试刷题神器 - 面试鸭open in new window

最近更新:
Contributors: weave