JVM GC算法、GC收集器简介

一些术语:

  • STW:Stop The World,指GC回收时暂停所有用户线程的现象

  • 并发:指GC线程与用户线程并发执行,不会产生STW

  • 并行:指GC线程是多线程并行执行,会产生STW

GC算法


标记-清除算法

分为标记和清除两个阶段(Mark-Sweep),这个算法是最基础的收集算法,先标记处所有需要回收的对象,标记完成后统一回收。主要存在的不足之处有两点

  • 效率问题:标记和清除两个过程的效率都不高

  • 空间问题:被标记回收的对象所在的内存位置是不连续的,因此在清理之后剩余空间也是不连续的,会产生空间碎片,导致后续不能分配连续空间而提前触发另一次垃圾回收

执行过程的示意图如下:

标记-清除算法示意图

复制算法

复制算法主要是将内存划分为容量相等的两块,每次只使用一块,需要回收的时候,将还需要存活的对象复制到另外一块内存当中,然后把原使用的那块内存直接清理掉,根据这种算法的方式,可以分析出其优缺点

优点:

  • 复制的时候是将对象挨个放入到空的内存块中,所以不会有空间碎片的问题

  • 实现起来较为简单,运行起来比较高效

缺点:

  • 内存空间的利用率不大,因为始终要保持一半内存的空闲状态

适用于低内存空间(因为利用率只有50%)、存活对象较少(复制效率会更高)的场景

复制算法示意图

标记-整理(-清除)算法

总体上与标记-清除算法一样,只是多了一个内存整理的过程,主要是为了解决上面提到的标记-清除算法引起的空间碎片的问题

标记-整理算法示意图

分代收集算法

分代收集算法并没有新的算法收集方式,只是将内存划分为新生代和老年代,对不同分代采取不同的垃圾收集算法,综合不同分代的特性以提高垃圾收集的效率。比如年轻代的特点是对象存活率低,即大量对象都会被回收,因此比较适合采用复制算法;而老年代的特点是对象存活率高,,所以比较适合采用标记-清理或标记-整理算法

GC策略


Serial

Serial收集器是发展最久的垃圾回收器,Serial单次本身的意思是“串行”,是一个单线程的收集器,作用于新生代,采用复制算法,在进行垃圾回收的时候会产生STW,主要适用于单核CPU的场景,可以避免多线程上下文切换带来的消耗

Serial收集过程示意图

对于Serial收集器有个疑问,在《深入理解Java虚拟机-JVM高级特性与最佳实践》第2版第76页有如下描述:

Serial收集器是最基本、发展历史最悠久的收集器,曾经(在JDK1.3.1之前)是虚拟机新生代收集的唯一选择。大家看名字就会知道,这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。

这里描述的Serial可能并不是只有一个垃圾收集线程,也就是说明可能是多线程,但是在ParNew收集器中又说ParNew是Serial收集器的多线程版本,感觉两处有些矛盾,因为没看过源码,所以不知道具体是怎样的情况。如有知情者请告知,万分感谢

Serial Old

Serial的老年代版本,即与Serial一样,采用单线程垃圾回收,不同的是采用的标记-整理算法

Serial Old收集过程示意图

ParNew

ParNew是Serial的多线程版本,即使用多线程进行垃圾回收,作用于新生代,采用复制算法,进行垃圾回收时会产生STW

ParNew收集过程示意图

Parallel Scavenge

Parallel Scavenge与ParNew类似,只是关注点不同。Parallel Scavenge主要关注吞吐量,吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)。提供了两个参数来控制吞吐量

  • -XX:MaxGCPauseMillis 控制最大垃圾收集停顿时间,大于0的毫秒数

  • -XX:GCTimeRatio 设置吞吐量大小,大于0且小于100的整数,即垃圾收集时间占总时间的比率

Parallel Old

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

CMS

以获取最短回收停顿时间为目标,作用于老年代,使用标记-清除算法,会产生空间碎片,但可以通过配置 -XX:+UseCmsCompactAtFullCollection (默认已开启)用于在CMS收集器要进行FullGC时开启内存碎片的整理

标记-清除的过程如下:

  • 初始标记:只标记GC Roots直接关联到的对象,速度快,会产生STW

  • 并发标记:根据GC Roots标记的对象进行追踪,标记出需要回收的对象。过程较长,但GC线程与用户线程并发执行,不产生 STW

  • 重新标记:修复并发标记过程中用户程序的运行导致标记的对象产生变动的的那部分对象,会产生STW

  • 并发清除:清除标记的对象,与用户线程并发执行,不产生STW

CMS运行过程示意图

G1

G1回收器除了综合其他回收器的回收方法之外,还将Java堆内存划分为很多个大小相等的独立区域Region,对新生代老年代也没有物理上的划分了,都是多个Region的集合。因此G1是作用在新生代和老年代的。从整体上看是使用的标记-整理算法,也避免了空间碎片的问题,从Region局部来看也有复制算法

  • 初始标记:只标记GC Roots直接关联到的对象,速度快,会产生STW

  • 并发标记:根据GC Roots标记的对象进行追踪,标记出需要回收的对象。过程较长,但GC线程与用户线程并发执行,不产生 STW

  • 最终标记:与CMS的重新标记类似,都是为了修复并发标记过程中用户程序的运行导致的变动的对象

  • 筛选回收:对各个Region的回收价值和回收成本进行排序,根据配置的期望GC停顿时间来决定回收计划,会产生STW,但也可以做到与用户程序并发执行

G1回收过程示意图

总结


GC回收器总结
回收器名称使用算法并发/并行适用CPU场景新生代老年代STW其他
Serial复制算法并行单核×整个GC过程会STW
Serial Old标记-整理算法并行单核×整个GC过程会STW此回收器同时也是CMS的备用回收器
ParNew复制算法并行多核×整个GC过程会STWSerial的多线程版本,有很大部分公用代码
Parallel Scavenge复制算法并行多核×整个GC过程会STW与ParNew类似,只是更关注吞吐量,可通过配置控制吞吐量
Parallel Old标记-整理算法并行多核×整个GC过程会STWParallel Scavenge的老年代版本
CMS标记-清除算法并发+并行多核×初始标记和重新标记会STW共3次标记,需要注意空间碎片。回收失败后会采用Serial Old备用回收器
G1标记-整理算法、复制算法并发+并行多核初始标记和最终标记会STW共3次标记,多Region,可通过配置控制吞吐量
GC组合总结

根据前面的介绍可以看出:

  • 新生代收集器:Serial、ParNew、Parallel Scavenge

  • 老年代收集器:Serial Old、Parallel Old、CMS

  • 整堆收集器:G1

可能的组合情况如下:

GC回收器组合场景

各个组合说明如下:

新生代策略老年代策略说明
SerialSerial OldSerial和Serial Old都是单线程进行GC,会产生STW,适用于单核场景
SerialCMS + Serial Old当CMS进行GC失败时,会自动使用Serial Old策略进行GC
ParNewCMS + Serial Old如果指定CMS回收器,则新生代会默认使用ParNew回收策略
ParNewSerial Old-XX:+UseParNewGC开启,新生代使用ParNew回收器时,老年代默认使用Serial Old回收策略
Parallel ScavengeSerial OldParallel Scavenge适用于后台持久运行的应用程序,如跑批应用
Parallel ScavengeParallel Old两者都是并行回收策略
G1G1需要留意G1的稳定性

参考资料


文中截图均来自《深入理解Java虚拟机-JVM高级特性与最佳实践》第2版

转载于:https://my.oschina.net/dengfuwei/blog/1627107

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值