JVM_11垃圾回收器(上)

GC的性能指标

吞吐量x:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间a+内存回收的时间b)  x=a/(a+b) -----越大越好

垃圾收集开销y:吞吐量的补数,垃圾收集所用时间与总运行时间的比例。y=b/(a+b) ------越小越好

  x+y=1

暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。 ------越短越好

收集频率:相对于应用程序的执行,收集操作发生的频率。

内存占用:Java 堆区所占的内存大小。

快速:一个对象从诞生到被回收所经历的时间。

上述红色部分构成“不可能三角”,不可能三项都满足。

7中经典的垃圾回收器

串行回收器: Serial、 Serial 0ld
并行回收器: ParNew、 Parallel Scavenge、 Parallel 0ld
并发回收器: CMS、G1

 

垃圾回收器之间的组合关系

红色虚线为jdk8和jdk9的变化,在jdk8之前是可以用红色线的,jdk8废弃了它(但是还可以用),jdk9彻底移除了这个关系(不可以再用了);

jdk14的变化为绿色的虚线,废弃了它(但是还可以用),并删除了CMS GC;

jdk8之前所有的线都是可以用的都是黑色实线。

下图的解释:新生代用ParNewGC,老年代就可以用CMS GC或者Serial Old GC

  CMS GC或者Serial Old GC的关系:因为CMS GC是并发的垃圾回收器,它在执行的同时用户线程还在执行,用户线程会制造新的垃圾,CMS需要提前回收,如果CMS回收的比较晚了,垃圾制造的速度比回收的速度还快,会形成回收失败的问题,一旦失败,则会用Serial Old GC作为后备方案-----备胎方案

 

7大收集器

一、Serial收集器 ----串行回收

Serial收集器是最基本、历史最悠久的垃圾收集器了。JDK1.3之前回收新生代唯一的选择。

Serial收集器作为HotSpot中Client模式下的默认新生代垃圾收集器。

Serial收集器采用复制算法、串行回收和" Stop-the-World"机制的方式执行内存回收。.

除了年轻代之外,Serial收集器还提供用于执行老年代垃圾收集的Serial 0ld收集器。Serial 0ld收集器同样也采用了串行回收和"Stop the World"机制, 只不过内存回收算法使用的是标记-压缩算法。

➢Serial 0ld是运行在Client模式下默认的老年代的垃圾回收器

➢Serial 0ld在Server模式下主要有两个用途:①与新生代的ParallelScavenge配合使用;②作为老年代CMS收集器的后备垃圾收集方案

 

这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop The World)。

优势

优势:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

如何设置使用Serial收集器

在HotSpot虚拟机中,使用-XX:+UseSeria1GC 参数可以指定年轻代和老年代都使用串行收集器。

  ➢等价于新生代用Serial GC, 且老年代用Serial 0ld GC

二、ParNew回收器----并行回收

如果说Serial GC是年轻代中的单线程垃圾收集器,那么ParNew收集器则是Serial收集器的多线程版本。

  ➢Par是Paralle1的缩写,New:只能处理的是新生代

ParNew收集器除了采用并行回收的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。ParNew收集器在年轻代中同样也是采用复制算法、"Stop-the-World"机制。

ParNew是很多JVM运行在Server模式下新生代的默认垃圾收集器。

 

 多cpu选择ParNew更高效,单cpu选择Serial

如何设置使用ParNew回收器

在程序中,开发人员可以通过选项"-XX: +UseParNewGC"手动指定使用ParNew收集器执行内存回收任务。它表示年轻代使用并行收集器,不影响老年代。

  - XX:ParallelGCThreads限制线程数量,默认开启和CPU数据相同的线程数。

三、Parallel Scavenge 回收器 ----吞吐量优先

HotSpot的年轻代中除了拥有ParNew收集器是基于并行回收的以外,Parallel Scavenge收 集器同样也采用了复制算法、并行回收和"Stop the World"机制。

那么Parallel收集器的出现是否多此一举?

➢和ParNew收集器不同,Parallel Scavenge收 集器的目标则是达到一个可控制的吞吐量,它也被称为吞吐量优先的垃圾收集器。

自适应调节策略也是Parallel Scavenge 与ParNew一个重要区别。

高吞吐量则可以高效率地利用CPU 时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务(交互任务需要低延迟,即暂停时间要短)。因此,常见在服务器环境中使用。例如,那些执行批量处理、订单处理、工资支付、科学计算的应用程序。

Parallel收集器在JDK1.6时提供了用于执行老年代垃圾收集的Parallel 0ld收集器,用来代替老年代的Serial 0ld收集器。

Parallel 0ld收集器采用了标记-压缩算法,但同样也是基于并行回收和”stop- the-World"机制。

在程序吞吐量优先的应用场景中,Parallel 收集器和Parallel 0ld收集器的组合,在Server模式 下的内存回收性能很不错。
在Java8中,默认是此垃圾收集器。

四、CMS回收器----低延迟

在JDK 1.5时期,HotSpot CMS (Concurrent -Mark - Sweep)收集器,这款收集器是HotSpot虚拟机中第一款真正意 义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程同时工作。

CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间。停顿时间越短( 低延迟)就越适合与用户交互的程序,良好的响应速度能提升用户体验。

  ➢目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合这类应用的需求。

CMS的垃圾收集算法采用标记-清除算法,并且也会"Stop- the-world"

不幸的是,CMS 作为老年代的收集器,却无法与JDK 1.4中已经存在的新生代收集器Parallel Scavenge 配合工作,所以在JDK 1. 5中使用CMS来收集老年代的时候,新生代只能选择ParNew或者Serial收集器中的一个。

在G1出现之前,CMS使用还是非常广泛的。一直到今天,仍然有很多系统使用CMS GC。

CMS的工作原理

CMS整个过程比之前的收集器要复杂,整个过程分为4个主要阶段,即初始标记阶段、并发标记阶段、重新标记阶段和并发清除阶段。

初始标记(Initial-Mark) 阶段:在这个阶段中,程序中所有的工作线程都将会因为“Stop-the-World"机制而出现短暂的暂停,这个阶段的主要任务仅仅只是标记出GCRoots能直接关联到的对象。一旦标记完成之后就会恢复之前被暂停的所有应用线程。由于直接关联对象比较小,所以这里的速度非常快。

并发标记(Concurrent-Mark) 阶段:从GC Roots的从初始标记阶段直接关联的对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。

 

重新标记(Remark) 阶段:由于在并发标记阶段中,程序的工作线程会和垃圾收集线程同时运行或者交叉运行,因此为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短。是STW的。

并发清除( Concurrent-Sweep)阶段:此阶段清理删除掉标记阶段判断的已经死亡的对象,释放内存空间由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。--------由于是使用的并发清除,因此会存在内存碎片问题。

 

 

尽管CMS收集器采用的是并发回收(非独占式),但是在其初始化标记和再次标记这两个阶段中仍然需要执行“Stop-the-World"机制暂停程序中的工作线程,不过暂停时间并不会太长,因此可以说明目前所有的垃圾收集器都做不到完全不需要“Stop-the-World”,只是尽可能地缩短暂停时间。

由于最耗费时间的并发标记与并发清除阶段都不需要暂停工作,所以整体的回收是低停顿的。

另外,由于在垃圾收集阶段用户线程没有中断,所以在CMS回收过程中,还应该确保应用程序用户线程有足够的内存可用。因此,CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,而是当堆内存使用率达到某一阈值时,便开始进行回收,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次"Concurrent Mode Failure"失败,这时虚拟机将启动后备预案:临时启用Serial 0ld收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。

CMS收集器的垃圾收集算法采用的是标记-清除算法,这意味着每次执行完内存回收后,由于被执行内存回收的无用对象所占用的内存空间极有可能是不连续的一些内存块,不可避免地将会产生一些内存碎片。那么CMS在为新对象分配内存空间时,将无法使用指针碰撞(Bump the Pointer) 技术,而只能够选择空闲列表(Free List) 执行内存分配。

有人会觉得既然Mark Sweep 会造成内存碎片,那么为什么不把算法换成Mark Compact——标记压缩算法呢 ?

因为当并发清除的时候,用Compact整理内存的话,原来的用户线程使用的内存还怎么用呢?要保证用户线程能继续执行,前提的它运行的资源不受影响。Mark Compact更适合“Stop the World"这种场景下使用。

优缺点

●CMS的优点:

➢并发收集
➢低延迟

●CMS的弊端:

1)会产生内存碎片,导致并发清除后,用户线程可用的空间不足。在无法分配大对象的情况下,不得不提前触发Full GC。

2) CMS收集器对cPU资源非常敏感。在并发阶段,它虽然不会导致用户停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。

3) CMS收集器无法处理浮动垃圾。可能出现“Concurrent Mode Failure" 失败而导致另一次Full GC的产生。在并发标记阶段由于程序的工作线程和垃圾收集线程是同时运行或者交叉运行的,那么在并发标记阶段如果产生新的垃圾对象,CMS将无法对这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有被及时回收,从而只能在下一次执行GC时释放这些之前未被回收的内存空间。

CMS收集器可以设置的参数

●-XX: +UseConcMarkSweepGC 手动指定使用CMS收集器执行内存回收任务。

  ➢开启该参数后会自动将-XX: +UseParNewGC打开。 即: ParNew (Young区用) +CMS (0ld区用) +Serial 0ld的组合。

●-XX:CMSlnitiatingOccupanyFraction 设置堆内存使用率的阈值,一旦达到该阈值,便开始进行回收。

五、Serial GC、Parallel GC、 Concurrent Mark Sweep GC小结

Hotspot有这么多的垃圾回收器,那么如果有人问,Serial GC、Parallel GC、 Concurrent Mark Sweep GC这三个Gc有什么不同呢?

请记住以下口令:

如果你想要最小化地使用内存和并行开销,请选Serial GC;

如果你想要最大化应用程序的吞吐量,请选Parallel GC;

如果你想要最小化GC的中断或停顿时间,请选CMS GC。

 

posted @ 2020-09-20 11:30  Mistolte  阅读(234)  评论(0编辑  收藏  举报