九又四分之三站台

0%

垃圾收集器

GC算法是内存回收的方法论,而垃圾收集器就是内存回收的具体实现。不同的厂商、不同版本之间的虚拟机所提供的垃圾收集器可能会有很大差别,并且一般都会提供参数供用户根据自己的需求组合出各个年代所使用的收集器。

CMS收集器

CMS(Concurrent Mark Sweep)收集器是一种以获得最短回收停顿时间为目标的收集器, 从名字上可以看出这是基于标记-清除算法实现的。 整个过程分为4个步骤

  1. 初始标记
  2. 并发标记
  3. 重新标记
  4. 并发清除

初始标记和重新标记会停止正在工作的线程。初始标记是标记出GC Roots能直接关联到的对象,速度很快,而重新标记是为了修正并发标记期间因用户程序运行而导致标记变动的那一份对象的标记记录,这一阶段造成的停顿会比初始标记要长。

CMS的优点是:并发收集、低停顿。但是它还有三个明显的缺点

  1. 对CPU资源非常敏感,在并发阶段虽然不会导致用户线程停顿(stop-the-world),但是会因为占用CPU资源导致应用程序变慢。 CMS默认的回收线程数是(CPU数量+3)/4,当CPU在4个以上时,至少会占用25%的资源,少于4个时会占用一般的资源。
  2. 无法处理浮动垃圾。 CMS并发清理阶段用户线程还在运行,会不断产生垃圾,这部分垃圾只能在下次GC时清理,这部分垃圾就称为“浮动垃圾”。这种情况可能会导致Full GC
  3. CMS是基于 标记-清除 算法来实现的收集器,在收集结束时会有大量的空间碎片,可能会提前触发Full GC

G1收集器

G1收集器是一款面向服务端应用的垃圾收集器,是用来可以替换掉CMS收集器的。它有一下的特点:

  1. 并行与并发: G1能充分利用多CPU来缩短stop-the-world时间。有些收集器执行GC时会停顿用户线程,G1可以通过并发的方式让Java程序继续执行。
  2. 分代收集: G1不需要其它收集器配合就能独立管理整个GC堆,但是它还是能采用不同的方式去处理新对象、已存活了一段时间的对象。
  3. 空间整合:G1从整体看是基于标记-整理算法实现的收集器,从局部看是复制算法。这意味着G1不会产生空间碎片。
  4. 可预测的停顿: G1能建立可预测的停顿时间模型,能让使用者制定在一个长度为M毫秒的时间内消耗在垃圾收集的时间不能超过N毫秒。

G1收集器的内存布局和其它收集器不同,它将整个Java堆分为多个大小相等的独立区域(Region),虽然还保留新生代和老年代的概念,但是不再是物理隔离,在G1收新生代和老年代都是回忆不放Region的集合。

G1收集器的运作大致可以分为以下几个步骤:

  1. 初始标记
  2. 并发标记
  3. 最终标记
  4. 筛选回收

初始标记:和CMS类似会标记GC Roots能直接关联到的对象,同时修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时能正确地用Region中创建的对象,这一阶段会短暂地停顿线程。

并发标记:进行可达性分析找出存活的对象,这个时间较长但是能和用户程序并行执行

最终标记:修正在并发标记期间产生变动的标记记录

Parallel Scavenge

java -XX:+PrintCommandLineFlags -version

可以通过以上命令看到自己的jdk用的是那一种垃圾收集器,我的个人电脑结果如下(Windows10系统, JDK1.8):

1
2
3
4
-XX:InitialHeapSize=265275648 -XX:MaxHeapSize=4244410368 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)

其中-XX:+UseParallelGC参数表示收集器类型,按照垃圾收集器的参数描述:新生代采用Parallel Scavenge,老年代采用Serial Old(PS Mark Sweep)

Parallel Scavenge是一个新生代的并行多线程收集器,使用的复制算法。Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即

吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间)

停顿时间越短越适合与用户交互的程序,吞吐量高则可以高效利用CPU资源,适合在后台运算而不太需要交互的程序。

Parallel Old

其实自从JDK7u4开始,就对 “-XX:+UseParallelGC”默认的老年代收集器进行了改进,改进使得HotSpot VM在选择使用”-XX:+UseParallelGC” 时,会默认开启 “ -XX:+UseParallelOldGC”,也就是说默认的老年代收集器是 Parallel Old。综上,JDK8中默认的选择是”-XX:+UseParallelGC”,是 Parallel Scavenge + Parallel Old组合。

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。