垃圾收集器

# Serial

Serial 垃圾收集器可以说是最古老的垃圾收集器,顾名思义他是串行进行垃圾收集的,在整个垃圾收集的时候回进行 STW 暂停掉整个用户线程的执行。

其在年轻代垃圾收集采用的是标记复制算法,

老年代的垃圾收集版本为 Serial Old 采用的是标记整理算法。

Serial 垃圾收集器在 gc 的整个过程单线程串行收集会进行长时间的 STW,所以对于用户体验很不好,但因为是单线程 STW 其收集过程不受任何影响收集过程简单高效,故有很高的单线程收集效率。

Serial Old 是 Serial 垃圾收集器的老年代版本,其主要作用是在 jdk 1.5 之前配合 parallel 一起使用,以及作为 CMS 垃圾收集器的后备方案。 开启参数:-XX:+UseSerialGC -XX:+UseSerialOldGC

# Parallel

Parallel 收集器与 Serial 收集器唯一的区别是其垃圾回收的过程是多线程的,默认线程个数为当前 cpu 核数,这个个数可以自己设置-XX:ParallelGCThreads 但一般不推荐修改。

可以理解为 Parallel 为 Serial 的多线程版

同时 Parallel 也提供了老年代版本的收集器来对老年代进行垃圾回收,同样其年轻代算法采用的是标记复制算法,老年代是标记整理算法。

-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代)

# ParNew

ParNew 与 Parallel 并没有本质上的区别,其主要是为了配合 CSM 的垃圾收集而提供的年轻代的垃圾收集器,其只有年轻代的收集版本,垃圾收集上与 Parallel 相同。

目前仅有 Serial 和 ParNew 可与 CSM 进行配合垃圾收集。

# CMS

CMS是一种以获取最短回收停顿时间为目标的收集器,它非常适合应用在互联网站或者B/S系统的服务器上。

作用范围:老年代

算法:标记清除

启用参数-XX:+UseConMarkSweepGC

默认回收线程数:(CPU数量 + 3)/4

Java9之后使用CMS垃圾收集器后,默认年轻代就为ParNew收集器,并且不可更改,同时JDK9之后被标记为不推荐使用,JDK14就被删除了。

# 垃圾收集步骤

  1. 初始标记(CMS initial mark):独占CPU,stop-the-world, 仅标记GCroots能直接关联的对象,速度比较快;

  2. 并发标记(CMS concurrent mark):可以和用户线程并发执行,通过GCRoots Tracing 标记所有可达对象;

  3. 重新标记(CMS remark):独占CPU,stop-the-world, 对并发标记阶段用户线程运行产生的垃圾对象进行标记修正,以及更新逃逸对象;

  4. 并发清理(CMS concurrent sweep):可以和用户线程并发执行,清理在重复标记中被标记为可回收的对象。

# 优点

  • 支持并发收集.

  • 低停顿,因为CMS可以控制将耗时的两个stop-the-world操作保持与用户线程恰当的时机并发执行,并且能保证在短时间执行完成,这样就达到了近似并发的目的.

# 缺点

  • CMS收集器对CPU资源非常敏感,在并发阶段虽然不会导致用户线程停顿,但是会因为占用了一部分CPU资源,如果在CPU资源不足的情况下应用会有明显的卡顿。

  • 无法处理浮动垃圾:在执行‘并发清理’步骤时,用户线程也会同时产生一部分可回收对象,但是这部分可回收对象只能在下次执行清理是才会被回收。

  • 如果在清理过程中预留给用户线程的内存不足就会出现Concurrent Mode Failure,一旦出现此错误时便会切换到SerialOld收集方式。

  • CMS清理后会产生大量的内存碎片,当有不足以提供整块连续的空间给新对象或晋升为老年代对象时又会触发FullGC

# 参数

-XX:+UseConcMarkSweepGC #启用cms
-XX:ConcGCThreads #并发的GC线程数
-XX:+UseCMSCompactAtFullCollection #FullGC 之后做压缩整理(减少碎片)
-XX:CMSFullGCsBeforeCompaction #间隔多少次 FullGC 之后压缩整理一次,默认是0,代表间隔 0 次 FullGC 后会进行压缩整理,即每次 FullGC 后都会进行整理
-XX:CMSInitiatingOccupancyFraction #当老年代使用达到该比例时会触发 FullGC(默认是92,这是百分比)
-XX:+UseCMSInitiatingOccupancyOnly #只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
-XX:+CMSScavengeBeforeRemark #在CMS GC 标记前启动一次 minor gc,目的在于减少老年代对年轻代的引用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在标记阶段
-XX:+CMSParallellnitialMarkEnabled #表示在初始标记的时候多线程执行,缩短STW
-XX:+CMSParallelRemarkEnabled #在重新标记的时候多线程执行,缩短STW;
1
2
3
4
5
6
7
8
9

# G1

G1重新定义了堆空间,打破了原有的分代模型,将堆划分为一个个区域。这么做的目的是在进行收集时不必在全堆范围内进行,这是它最显著的特点。

区域划分的好处就是带来了停顿时间可预测的收集模型:用户可以指定收集操作在多长时间内完成。即G1提供了接近实时的收集特性。

G1会通过一个合理的计算模型,计算出每个Region的收集成本并量化,这样一来,收集器在给定了“停顿”时间限制的情况下,总是能选择一组恰当的Regions作为收集目标,让其收集开销满足这个限制条件,以此达到实时收集的目的。

# 垃圾收集步骤

  • 初始标记(Initial Marking):仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这阶段需要停顿线程,但耗时很短。

  • 并发标记(Concurrent Marking):是从GC Roots开始堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行。

  • 最终标记(Final Marking):是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并行执行。

  • 筛选回收(Live Data Counting and Evacuation):首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。这个阶段也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。

# 与CMS相比,G1的优点

  1. G1不会产生内存碎片,CMS使用的是空闲列表,而G1使用的是Region列表

  2. G1可以精确控制停顿时间,CMS只能粗略控制

控制G1回收垃圾的时间:-XX:MaxGCPauseMillis=200

上次更新: 2024/06/17, 11:58:14
最近更新
01
go-admin-ui项目仿写练手1-登录页
06-29
02
maven依赖问题
06-17
03
JVM相关命令
02-21
更多文章>