JVM实战:垃圾收集器及其适用场景

在这里插入图片描述

垃圾收集器

在这里插入图片描述
图中展示了七种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明它们可以搭配使用。在JDK8时将Serial+CMS,ParNew+Serial Old这两个组合声明为废弃,并在JDK9中完全取消了这些组合的支持

在这里插入图片描述
并行和并发都是并发编程中的专业名词,在谈论垃圾收集器的上下文语境中, 它们可以理解为

并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态

并发(Concurrent):指用户线程与垃圾收集线程同时执行

串行收集器

Serial收集器

新生代,标记-复制算法,单线程。进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束
在这里插入图片描述

Serial Old收集器

老年代,标记-整理算法,单线程,是Serial收集器的老年代版本

用处有如下2个

  1. 在JDK5以及之前的版本中与Parallel Scavenge收集器搭配使用
  2. 作为CMS收集器发生失败时的后备预案,在并发收集发生Concurrent Mode Failure时使用

并行收集器

Parallel Scavenge收集器

新生代,标记复制算法,多线程,主要关注吞吐量

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

Parallel Old收集器

老年代,标记-整理算法,多线程,是Parallel Scavenge收集器的老年代版本

在注重吞吐量或者处理器资源较为稀缺的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器这个组合
在这里插入图片描述

ParNew收集器

ParNew本质上是Serial收集器的多线程并行版本
在这里插入图片描述

CMS收集器

老年代,标记-清除算法,多线程,主要关注延迟

运作过程分为4个步骤

  1. 初始标记(CMS initial mark)
  2. 并发标记(CMS concurrent mark)
  3. 重新标记(CMS remark)
  4. 并发清除(CMS concurrent sweep)

在这里插入图片描述
5. 初始标记:标记一下GC Roots能直接关联到的对象,速度很快(这一步会发生STW)
6. 并发标记:从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集一起并发运行
7. 重新标记:为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录(就是三色标记法中的增量更新,这一步也会发生STW)
8. 并发清除:清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以看这个阶段也是可以与用户线程同时并发的

因为目前ParNew+CMS的组合最常用,因此我们就接着来看看CMS有哪些问题?

1.并发回收垃圾导致CPU资源紧张

并发标记和并发清理阶段,垃圾回收线程和系统工作线程同时工作,会导致有限的CPU资源被垃圾回收线程占用了一部分

2.无法处理浮动垃圾

在并发清理阶段,CMS回收的是之前标记好的对象,但是这个阶段系统一直在运行,有可能会产生新的垃圾对象,这种垃圾对象就是“浮动垃圾”,浮动垃圾只能等到下一次GC才会被回收

3.Concurrent Mode Failure导致垃圾收集器切换到SerialOld

如果在CMS垃圾回收期间,程序要放入老年代的对象大于了可用内存空间,会发生Concurrent Mode Failure,就是说并发垃圾回收失败,我一边回收,你一边把对象放入老年代,内存都不够用了。此时就会用SerialOld替换CMS,直接 Stop the World 进行垃圾回收,一次性把垃圾对象都回收掉

4.CMS用的是标记清除算法,会导致大量的内存碎片

G1收集器

G1收集器最大的特点就是把堆划分成多个大小相等的Region
在这里插入图片描述

G1也会有新生代和老年代的概念,但是是逻辑上的概念,并不是物理上的概念。一个Region有可能属于新生代有可能属于老年代,新生代和老年代的内存区域是不停变动的,由G1自动控制

在G1之前的垃圾收集器,当发生GC的时候,要么回收整个新生代,要么回收整个老年代。而G1可以面向堆内存的任何部分进行回收,Region是单次回收的基本单元

有了Region作为单次回收的基本单元,就能建立可预测停顿时间模型,即我们可以通过设置 -XX:MaxGCPauseMillis 参数来控制STW的时间,G1尽可能的在这个时间内完成垃圾收集,并且G1会优先回收价值收益大的Region(即花的时间少并且回收的垃圾还多)
在这里插入图片描述
G1收集器的运行过程如下

  1. 初始标记:标记一下GC Roots能直接关联到的对象,速度很快(这一步会发生STW)
  2. 并发标记:从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集一起并发运行
  3. 最终标记:为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录(就是三色标记法中的原始快照,这一步也会发生STW)
  4. 筛选回收:根据用户期望的停顿时间,回收部分Region。首先把要回收的Region中的存活对象复制到空的Region,在清理掉旧Region的空间

与CMS的“标记-清除”算法不同, G1从整体来看是基于“标记-整理”算法实现的收集器, 但从局部(两个Region之间) 上看又是基于“标记-复制”算法实现, 无论如何, 这两种算法都意味着G1运作期间不会产生内存空间碎片, 垃圾收集完成之后能提供规整的可用内存。 这种特性有利于程序长时间运行, 在程序为大对象分配内存时不容易因无法找到连续内存空间而提前触发下一次收集

总结

收集器 收集对象和算法 收集器类型 说明 适用场景
Serial 新生代,复制算法 单线程 简单高效;适合内存不大的情况
ParNew 新生代,复制算法 并行的多线程收集器 ParNew垃圾收集器是Serial收集器的多线程版本 搭配CMS垃圾回收器的首选
Parallel Scavenge吞吐量优先收集器 新生代,复制算法 并行的多线程收集器 类似ParNew,更加关注吞吐量,达到一个可控制的吞吐量 本身是Server级别多CPU机器上的默认GC方式,主要适合后台运算不需要太多交互的任务
收集器 收集对象和算法 收集器类型 说明 适用场景
Serial Old 老年代,标记整理算法 单线程 Client模式下虚拟机使用
Parallel Old 老年代,标记整理算法 并行的多线程收集器 Paraller Scavenge收集器的老年代版本,为了配置Parallel Svavenge的面向吞吐量的特性而开发的对应组合 在注重吞吐量以及CPU资源敏感的场合采用
CMS 老年代,标记清除算法 并行与并发收集器 尽可能的缩短垃圾收集时用户线程停止时间;缺点在于,1.内存碎片,2.需要更多CPU资源,3.浮动垃圾问题,需要更大的堆空间 重视服务的相应速度,系统停顿时间和用户体验的互联网网站或者B/S系统。互联网后端目前cms是主流的垃圾回收器
G1 跨新生代和老年代;标记整理 并行与并发收集器 JDK1.7才正式引入,采用分区回收的思维,基本不牺牲吞吐量的前提下完成低停顿的内存回收;可预测的停顿是其最大的优势

参考博客

[1]https://zhuanlan.zhihu.com/p/375977480
[2]https://www.jianshu.com/p/671495682e46
[3]https://www.zhihu.com/question/431264970/answer/1586591296

猜你喜欢

转载自blog.csdn.net/zzti_erlie/article/details/123380913
今日推荐