对于上一篇所讲垃圾回收算法,是有专门的垃圾收集器实现算法的。
JVM说到底还是规范,垃圾收集器品种也有很多。下面讨论几种使用较多的垃圾收集器
- 使用
java -XX:+PrintCommandLineFlags -version
来查看你所使用的垃圾收集器 - 使用
java -XX:+PrintFlagsFinal -version
查看所有配置项
本人现在的jdk版本为14,使用了G1垃圾收集器(JVM默认)
在本文撰写时间,比较受关注、典型的垃圾收集器有这几种
首先对这个图为不熟悉JVM的兄弟做个解读嗷!
前面的章节中,有提到分代收集的算法
一般情况下,分为“老年代”与“新生代”
起初对这两个“代”进行分开设计收集器进行处理。
从jdk1.7开始,
G1垃圾收集器崭露头角,到JAVA11中出现的ZGC,
这两款垃圾收集器不再将内存划分为上面的两代
如G1垃圾收集器,将内存分为多个大小相等的独立区域,老年代与新生代不再物理隔离,都是一部分Region(不需要连接)的集合。
新生代收集器
Serial收集器(新生代)
Serial收集器是最基本、发展历史最悠久的收集器。
新时代复制算法
单线程收集器,在进行垃圾收集时,必须暂停其他所有的工作线程,直到他收集结束——“Stop The World”
图为Serial/Serial Old收集器
缺点:Stop The World
优点:简单高效,对于限定单个CPU的环境,Serial收集器没有线程交互开销,效率最高。小内存环境适用。
适合Client模式下的虚拟机。
ParNew收集器(新生代)
Serial收集器的多线程版本,包括Serial收集器可用的所有控制参数。
使用复制算法
进行垃圾回收需要暂停所有用户线程。
图为ParNew/Serial Old收集器
运行在Server模式下的虚拟机中首选的新生代收集器。
目前唯一一款能与CMS收集器配合工作。
单CPU效果甚至不如Serial,适应多核。
相关参数
- 使用
-XX:+UseConcMarkSweepGc
(使用CMS)选项后默认的新生代收集器。 - 使用
-XX:+UseParNewGc
强制指定 - 使用
-XX:ParallelGCThreads
限制垃圾收集线程数
Parallel Scavenge 收集器(新生代)
使用复制算法,并行多线程收集器
设计目标:可控制的吞吐量。
吞吐量=运行用户代码/(运行用户代码时间+垃圾收集时间)
GC停顿时间缩短是以牺牲吞吐量和新时代空间换取的
“吞吐量优先”收集器
特殊:通过开启-XX:UseAdaptiveSizePolicy
可以让虚拟机根据当前系统运行情况收集性能监控信息,调整合适的停顿时间或最大的吞吐量——“GC自适应的调节策略”
相关参数:
- 使用
-XX:MaxGCPauseMillis
控制最大垃圾收集停顿时间,值大于0 - 使用
-XX:GCTimeRatio
参数设置吞吐量大小,值在0-100中间的整数 - 使用
-XX:UseAdaptiveSizePolicy
开启GC自适应
老年代收集器
Serial Old(老年代)
Serial收集器的老年代版本,单线程收集器。
“标记-整理”算法
适用Client模式,Server模式可以作为CMS的后备预案,发生Concurrent Mode Failure时使用
Parallel Old 收集器(老年代)
Parallel Old 是Parallel Scavenge收集器的老年代版本
多线程+“标记-整理”
吞吐量优先,jdk1.8的默认选择
CMS收集器(老年代)
CMS(Concurrent Mark Sweep)
目标:获取最短回收停顿时间为目标。
适应需求:B/S系统的服务端,重视服务响应速度,希望系统停顿时间最短。
标记-清除算法
四个步骤:
- 初始标记(CMS initial mark)
- 并发标记(CMS concurrent mark)
- 重新标记(CMS remark)
- 并发清除(CMS concurrent sweep)
初始标记、重新标记需要“Stop The World”
- 初始标记标记GC Roots能直接关联到的对象,速度很快,
- 并发标记阶段进行GC Roots Tracing的过程
- 重新标记阶段修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,时间很短,但长于初始标记
缺点:
- 对CPU资源敏感。占用了一个线程执行回收工作
- 无法处理浮动垃圾(Floating Garbage),由于CMS并发清理阶段用户线程还在运行,伴随运行会产生新的垃圾,这部分垃圾本次无法清除,称为“浮动垃圾”
- 可能出现“Concurrent Mode Failure”失败导致Full GC,在JDK1.5,老年代空间使用68%后CMS激活,JDK1.6中设置为92%。若CMS运行期间预留的内存无法满足程序需要,就会出现“Concurrent Mode Failure”,虚拟机将启动后备预案:临时启用Serial Old收集器
- “标记-清除”算法产生空间碎片。
相关参数:
- 使用
-XX:CMSInitiatingOccupancyFration
设置CMS触发百分比。 - 使用
-XX:+UseCMSCompactAtFullCollection
在CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理过程。 - 使用
-XX:CMSFullGCsBeforeCompaction
设置执行多少次不压缩的Full GC后,跟着来一次压缩的,默认为0。
全局的垃圾收集器
G1收集器
一款面向服务端应用的垃圾收集器
特点:
- 并行与并发:利用多核环境来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿来执行GC的操作,通过并发让Java程序继续执行。
- 分代收集:采用不同的方式处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
- 空间整合:G1从整体看是基于“标记-整理”算法,从局部(两个Region之间)看是基于“复制”算法的实现。这意味着不会产生空间碎片,提供规整的可用内存。
- 可预测的停顿:G1除了追求低停顿外,还能建立可预测的停顿时间模型,让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集器上的时间不得超过N毫秒。
老年代与新生代不再物理隔离
使用G1收集器时,Java堆的内存布局就是与其他收集器有很大的区别,将整个内存分为多个大小相等的独立区域(Region),老年代新生代不再物理隔离,都是一部分Region(不需要连续)的集合。
优先回收价值最大Region——Garbage-Frist
有计划地避免整个Java堆中全区域垃圾收集。跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收最大Region。Region划分内存空间以及有优先级的区域,保证G1收集器能建立可预测的停顿时间模型,在有限时间内获取尽可能高的收集效率。
回收步骤
类似CMS
- 初始标记
- 并发标记
- 最终标记:虚拟机在并发标记期间,将对象变化记录在线程Remembered Set中,最终标记阶段把数据合并。
- 筛选回收:根据用户期望回收
使用-XX:MaxGCPauseMillis=50
设置50毫秒内完成。
下面这篇文章比较了G1收集器的在Java11带来的效率
https://www.oschina.net/news/103704/how-much-faster-is-java-11
目前选择并行GC效率似乎更好