JVM垃圾收集器!

基本概念

串行Serial收集:

  • 所有用户线程停止,单条GC线程回收堆的情况被称为串行回收。

并行Parallel收集:

  • 所有用户线程停止,多条GC线程回收堆的情况(需多核CPU支持)。

独占Monopoly执行:

  • GC工作时,GC线程会抢占所有资源执行,整个应用程序会被停止。

并发Concurrent执行:

  • 用户线程和GC线程同时(交替)执行的情况,不会停下某类线程。

吞吐量:

指CPU用于执行用户代码的时间与CPU总耗时的比值,计算公式为:

  • 吞吐量 = 用户代码执行总时长 /(用户代码执行总时长 + 垃圾回收总时长)。

如JVM在线上执行了100min,其中执行用户代码花费了99min,垃圾回收总用时1min

  • 那么吞吐量则为99min/(99min+1min)=99%

写屏障:

指在赋值操作前后加入一些逻辑处理(类似于SpringAOP面向切面前后置处理的思想)。

GC组合方案:

追求低延迟,用户交互度较为频繁:采用:ParNew + CMS

追求高吞吐,后台计算工作较多:采用:Parallel Scavenge + Parallel Old

Serial收集器

单线程的GC收集器,被称为串行收集器。

该收集器在发生GC时,会产生STW,也就是会停止所有用户线程。

由于会停止其他用户线程,所以在执行GC时并不会出现线程间的切换。

在单颗CPU的机器上,它的清理效率非常高。

一般来说,采用Client模式运行的JVM,选取该款收集器作为内嵌GC是个不错的选择。

ParNew收集器(多线程)

Serial收集器的多线程版本,是作用于新生代区域的收集器。

  • 在整个实现上,除开GC收集阶段会使用多条线程回收外,其他实现几乎与Serial收集器大致相同。

该款GC收集器因为采用了多线程,所以需要多核CPU的支持。

该收集器会根据CPU核数,开启不同的GC线程数,从而达到最优的垃圾回收效果(也可通过-XX:ParallelGCThreads参数指定)。

  • 若是单核的机器上运行时,其效率可能不如Serial
  • 如果是以Server模式运行的程序,而老年代又采用了CMS收集器,那么新生代搭配ParNew是个不错的选择。

Parallel Scavenge收集器(多线程)

一款作用于新生代的多线程GC收集器,但与ParNew收集器不同的是:

  • ParNew通过控制GC线程数量来缩短程序暂停时间,更关心程序的响应时间
  • Parallel Scavenge更关心的是程序运行的吞吐量,更注重一段时间内,用户代码执行时长与程序执行总时长的占比。

PS收集器可以通过-XX:MaxGCPauseMillis-XX:GCTimeRatio参数精准控制GC发生时的时间以及吞吐量占比。

PS收集器还可以通过开启-XX:+UseAdaptiveSizePolicy参数,让JVM启动自适应的GC调节策略。

开启该参数后,JVM会根据当前系统的运行状态调整吞吐比与GC时间,从而确保能够提供最合适的停顿时间和吞吐量。

Serial Old(MSC)收集器(单线程)

Serial Old(MSC)Serial收集器相同,同样是一款单线程串行回收的收集器。

不同的是:MSC是一款作用于年老代空间的收集器,它采用标记-整理算法对年老代空间进行回收。

同时,该款收集器也可作为CMS的备用收集器使用。

Parallel Old收集器(多线程)

Parallel Old则是Parallel Scavenge收集器的年老代版本,同样采用多线程进行并行收集,其内部采用标记-整理算法。

与新生代的PS收集器相同的是:PO同样追求的是吞吐量优先

ShenandoahGC

JDK12推出了ShenandoahGC收集器,它与G1、ZGC收集器一样,都是基于分区结构实现的一款收集器。

和ZGC对比,相同的是:它们的停顿时间都不会受到堆空间大小的影响。

ZGC收集器

JDK11时推出垃圾回收器ZGC,是一款基于分区概念的内存布局GC器。

  • 是真正意义上的不分代收集器,无论是从逻辑上,还是物理上都不再保留分代的概念。

ZGC主要是超低延迟与吞吐量,在实现时,ZGC也会在尽可能堆吞吐量影响不大的前提下。

实现在任意堆内存大小下都可以把垃圾回收的停顿时间限制在10ms以内的低延迟。

引入ZGC的目的主要有如下四点:

奠定未来GC特性的基础。

为了支持超大级别堆空间(TB级别),最高支持16TB

在最糟糕的情况下,对吞吐量的影响也不会降低超过15%。

GC触发产生的停顿时间不会偏差10ms