JVM之垃圾回收 III ——垃圾回收算法、垃圾回收器(更新中)

一、垃圾回收算法

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

1)最基础的收集算法,老年代收集算法:之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进的。

2)算法分为“标记”和“清除”两个阶段:

  • 标记:首先标记出所有需要回收的对象
  • 清除:在标记完成后统一回收所有被标记的对象

JVM之垃圾回收 III ——垃圾回收算法、垃圾回收器(更新中)

3)两个不足:

  1. 效率问题,标记和清除两个过程的效率都不高。
  2. 空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

2、复制算法(Copying算法)

1)算法思想: 将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

对于复制算法的过程可以参考上一篇博客中的新生代垃圾回收过程,博客链接:JVM之垃圾回收 II.

2)复制算法是新生代的收集算法

3)优缺点:

  • 优点:实现简单,运行高效,不存在内存碎片问题。
  • 缺陷:内存使用率不高——50%

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

1)算法分为“标记”和“整理”两个阶段:

  • 标记:首先标记出所有需要回收的对象
  • 整理: 让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。

JVM之垃圾回收 III ——垃圾回收算法、垃圾回收器(更新中)

2)老年代收集算法

提高了内存的利用率,适合在手机对象存活时间较长的老年代。

4、分代收集算法(Generatinal Collection)

当前JVM垃圾收集都采用的是"分代收集(Generational Collection)"算法,这个算法并没有新思想,只是根据对象存活周期的不同将Java堆分为新生代和老年代。

新生代划分:
新生代中98%的对象都是"朝生夕死"的,所以并不需要按照复制算法所要求1 : 1的比例来划分内存空间,而是将内存(新生代内存)分为一块较大的Eden(伊甸园)空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor(两个Survivor区域一个称为From区,另一个称为To区域)。HotSpot默认Eden与Survivor的大小比例是8 : 1,也就是说Eden :Survivor From : Survivor To = 8 : 1 : 1。所以每次新生代可用内存空间为整个新生代容量的90%,只有10%的内存会被”浪费“。
JVM之垃圾回收 III ——垃圾回收算法、垃圾回收器(更新中)

  • 新生代中,每次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法
  • 老年代中,对象存活率高、没有额外空间对它进行分配担保,就必须采用 "标记-清理"或者"标记-整理"算法

二、垃圾回收器

在垃圾收集器的学习中,有两个概念需要先说明:

  • 并行(Parallel) : 指多条垃圾收集线程并行工作,用户线程仍处于等待状态。
  • 并发(Concurrent) :指用户线程与垃圾收集线程同时执行(不一定并行,可能会交替执行),用户程序继续运行,而垃圾收集程序在另外一个CPU上。

历史的发展原因:垃圾回收算法也在发展,所以会有改进的垃圾回收器。HotSpot虚拟机提供了7种作用于不同分代的收集器,分别是:

  • Serial收集器(新生代收集器,串行GC)
  • ParNew收集器(新生代收集器,并行GC)
  • Parallel Scavenge收集器(新生代收集器,并行GC)
  • Serial Old收集器(老年代收集器,串行GC)
  • Parallel Old收集器(老年代收集器,并行GC)
  • CMS收集器(老年代收集器,并发GC)
  • G1收集器(全区域的垃圾回收器)

在上述垃圾收集器中,前三个为新生代垃圾收集器,再紧接三个是老年代垃圾收集器,最后一个是Full GC的垃圾收集器。我们接着一一介绍。

1、Serial收集器

1)特性:

  • 单线程
  • 复制算法
  • Stop The World(STW)

2)应用场景:Client模式下的默认新生代收集器。
原因: 在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代(仅仅是新生代使用的内存,桌面应用基本上不会再大了),停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不是频繁发生,这点停顿是可以接受的。所以,Serial收集器对于运行在Client模式下的虚拟机来说是一个很好的选择。

单个CPU的环境(单线程)来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

1、ParNew收集器

ParNew收集器是Serial收集器的多线程版本!!! 它相当于Serial收集器除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一样,在实现上,这两种收集器也共用了相当多的代码。

特性:

  • 多线程
  • 复制算法
  • Stop The World(STW)

应用场景:
搭配CMS收集器,在用户体验优先的程序中使用: