常用垃圾回收器对比
[TOC]
背景
两种标记算法
算法 | 说明 | 特点 |
---|---|---|
引用计数法 | 在堆内存中分配对象时,会为对象分配一段额外的空间,这个空间用于维护一个计数器: 如果有一个新的引用指向这个对象,则计数器+1 如果指向该对象的引用被置空或指向其它对象,则计数器-1 当计数器的值为0时,则自动删除这个对象。 |
1.循环引用问题 2.多线程维护计数器问题 |
可达性分析法 | 以根集合(GCRoot)作为起始点,从这些节点出发,根据引用关系开始搜索,所经过的路径称为引用链,当一个对象没有被任何引用链访问到时,则证明此对象是不活跃的,可以被回收 |
什么是循环引用?
有对象 A 和对象 B,对象 A 中含有对象 B 的引用,对象 B 中含有对象 A 的引用。此时,对象 A 和对象 B 的引用计数器都不为 0。但是在系统中却不存在任何第 3 个对象引用了 A 或 B。也就是说,A 和 B 是应该被回收的垃圾对象,但由于垃圾对象间相互引用,从而使垃圾回收器无法识别,引起内存泄漏。
可以作为GCRoot的对象
序号 | 对象 | 范围 | 描述 |
---|---|---|---|
1 | 方法区中的类静态属性引用的对象 | 全局对象 | Class对象。 |
2 | 方法区中常量引用的对象 | 全局对象 | 例如字符串常量池,本身初始化后不再改变。 |
3 | 虚拟机栈中(栈帧中的本地变量表)引用的对象 | 执行上下文 | 属于执行上下文中的对象,线程在执行方法时,会将方法打包成一个栈帧入栈执行,方法里用到的局部变量会存放到栈帧的本地变量表中。只要方法还在运行,还没出栈,就意味这本地变量表的对象还会被访问,GC就不应该回收,所以这一类对象也可作为GC Roots。 |
4 | 本地方法栈中 JNI(Native 方法) 引用的对象 | 执行上下文 | 类似虚拟机栈中引用对象,区别是native方法。 |
5 | 被同步锁持有的对象 | 执行上下文 | 当前有线程持有对象锁的情况下,被synchronized锁住的对象不能回收,否则会导致锁失效。 |
什么是GCRoot?
JVM确定当前绝对不能被回收的对象。
常见垃圾回收算法
算法 | 说明 | 适用场景 | 特点 |
---|---|---|---|
标记-复制 | 标记阶段:从根节点开始标记所有被引用的对象(活动对象) 复制阶段:内存被划分为两个大小相同区域,每一次只使用一个区域,回收时将存活对象复制到另一个区域,再清空原区域 |
适用于年轻代:存活对象较少的情况 | 需要两倍内存空间 需要复制移动对象 |
标记-清除 | 标记阶段:从根节点开始标记所有被引用的对象(活动对象) 清除阶段:将未标记的对象清除 |
适用于老年代:存活对象较多的情况 | 因为清理后内存空间不连续,容易产生内存碎片 |
标记-整理 | 标记阶段:从根节点开始标记所有被引用的对象(活动对象) 整理阶段:将存活对象向头部移动,然后清除尾部以外的内存 |
适用于存活对象较少的情况 | 不会产生内存碎片 效率低于标记-清除算法 |
主要的垃圾回收性能指标
指标 | 描述 |
---|---|
更高的GC效率 | GC线程串行独占式运行,没有线程切换开销,且运行时暂停所有用户线程,能以最高效率回收非存活对象。 |
更高的吞吐量 | 吞吐量指在应用程序的生命周期内,应用程序所花费的时间和系统总运行时间的比值。 GC线程运行时会暂停用户线程,用户线程暂停时间越短,用户线程运行时间越长,系统吞吐量越高。 |
更短的STW停顿时间 | GC线程并发运行,用户线程单次暂停时间越短,系统响应时间越短。 |
什么是STW?
STW全称Stop The World,即GC线程运行时暂停其它用户线程,为了保持对象引用关系不变,GC时能够准确的标记出存活对象。
常见垃圾回收器
回收器 | 内存区间 | 回收算法 | GC线程运行方式 | GC线程工作模式 | STW停顿时间 | 优先指标 | 适用CPU核数 | 主要特性 | 描述 |
---|---|---|---|---|---|---|---|---|---|
Serial | 新生代 | 标记-复制 | 串行 | 独占 | 停顿 | 更高的GC效率 | 单核 | 1.GC时会暂停所有应用线程 | 适合堆空间较小、单核CPU串行的收集器 |
ParNew | 新生代 | 标记-复制 | 并行 | 独占 | 停顿 | 更短的STW停顿时间 | 多核 | 1.多核CPU情况下,GC线程并行以减少STW耗时,单核CPU则会因为线程切换效率更差 | 相当于Serial的多线程版本,一般和CMS配合使用 |
Parallel Scavenge | 新生代 | 标记-复制 | 并行 | 独占 | 停顿 | 更高的吞吐量 | 多核 | 1.能控制最大STW停顿时间,以高效利用CPU实现更高的吞吐量 | 适合多核CPU,不需要太多交互的场景并行的收集器 |
Serial Old(PS MarkSweep) | 老年代 | 标记-整理 | 串行 | 独占 | 停顿 | 更高的GC效率 | 单核 | 1.GC时会暂停所有应用线程 2.老年代存活对象较多,因此整理而不是复制 |
Serial的老年代版本 适合单核CPU串行的收集器(Parallel MarkSweep 和 Serial Old 实现相似) |
Parallel Old | 老年代 | 标记-整理 | 并行 | 独占 | 停顿 | 更高的吞吐量 | 多核 | 1.多核CPU情况下,GC线程并行以减少STW耗时 | Parallel Scavenge的老年代版本 |
CMS | 老年代 | 标记-清除 | 并行 | 并发 | 小停顿 | 更短的STW停顿时间 | 多核 | 1.拆分标记阶段以降低STW停顿时间 2.清除时内存碎片较多 |
为了GC线程与用户线程并发,共用内存,故采用清除算法而不是复制算法 |
G1 | 标记-整理/标记-复制 | 并行 | 并发 | 可预测小停顿 | 更短的STW停顿时间 | 多核 | 1.可预测STW停顿时间 | 适合多核CPU不划分连续的年轻代和老年代;通过计算老年代对象效益率,优先回收最大效益对象,来预测停顿时间 | |
ZGC | 标记-复制 | 并行 | 并发 | 更小停顿 | 更短的STW停顿时间 | 多核 | 1.极短的STW停顿时间,可能降低吞吐量 | 适合对长尾请求P99要求高的系统 |
CMS
什么是CMS?
CMS全称Concurrent Mark Sweep,是一款以低停顿为目标的垃圾回收器,这个回收器是一款真正意义的并发收集器。
CMS是针对老年代的垃圾回收器,采用了标记-清除算法。
CMS的GC过程
序号 | GC步骤 | 是否STW | 与用户线程 | 说明 |
---|---|---|---|---|
1 | 初始标记 | STW | GC线程可并行 用户线程停顿 |
从GC Root出发,标记所有GC Root直接关联的老年代存活的对象(可达性分析) 只有STW的时候才能准确标记到存活对象 |
2 | 并发标记 | 否 | GC线程与用户线程并发 | 从初始标记的对象开始,遍历整个对象引用链,标记GC Root最终可达存活对象 并发标记与用户线程并发过程中,部分引用关系已变化,JVM将已变化的区域标记为“脏区”,预先找到“脏区”并刷新引用关系 |
3 | 最终标记/重新标记 | STW | 用户线程停顿 | 重新从GC Root出发标记所有的存活对象 |
4 | 并发清理 | 否 | GC线程与用户线程并发 | 清理非存活对象,老年代的存活对象较多,因此只清理非存活对象 |
5 | 并发重置 | 否 | GC线程与用户线程并发 | 重新调整堆大小,重置卡表的标记等 |
卡表和脏区:
YGC时为了标记活动标记对象除了从GC Root开始扫描外, 另外还有老年代里也会引用新生代对象。所以正常来说还要扫描一次老年代,如果是扫描整个老年代(相当于将整个老年代作为GC Root)这将会随着堆的增大变得越来越慢,特别是现在内存都越来越大了。所以为了提升性能就引入卡表。
逻辑上把老年代内存分成一个个大小相等的卡片,然后对每个卡片准备一个与其对应的标记位,并将这些位集中起管理就好像一个表格一样,改写对象引用是从老年代指向新生代时,在老年代对应的卡片标记位上设置标志位即可,通常这样的卡片我们称之为“脏区”。
btye[i] = 1 就表示第i + 1 卡片所在内存上有指向新生代引用的老年代对象,这时只要tracing这个卡片上的对象即可。
如果每个card大小的是128字节(1024位,)那卡表就只占整个老年代的1/1024之一。所以遍历卡表的时间会远比遍历整个老年代快得多。
CMS的特点
序号 | 特点 |
---|---|
1 | 以降低响应速度优先,只在初始标记阶段+最终标记阶段会STW,其它标记阶段GC线程和用户线程并发运行,尽可能减少标记阶段STW时间 |
2 | 无法控制STW的耗时,耗时与扫描对象个数正相关,耗时随堆内存的增大而增加 |
3 | 清除非存活对象会产生内存碎片,不过CMS会在FGC进行内存压缩,当回收的堆空间不够,则会回退到Serial Old进行标记-整理,最终导致STW变长 |
4 | 因为标记阶段GC线程并发,会占用CPU资源,最终导致吞吐量下降 |
5 | 因为标记阶段为了保证标记到所有存活对象,一些非存活对象也被标记成了存活对象,只能等下一次GC |
G1
什么是G1?
G1的内存模型
G1的GC过程
ZGC
什么是ZGC?
ZGC收集器是一款基于Region内存布局的,(暂时)不设分代的,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的,以低延迟为首要目标的一款垃圾收集器。
ZGC的GC过程
总结
CMS、G1和ZGC特性对比
CMS | G1 | ZGC | |
---|---|---|---|
某运价系统从PS Scavenge和PS MarkSweep切换为ZGC后的效果
请求平均耗时(缺失P99监控),从15ms降低到10ms,效果明显
GC次数增多,GC时间从35ms降低到3ms
新的ZGC Pauses平均次数=3.9次/秒
新的ZGC Pauses GC平均耗时=3.31ms
旧的年轻代PS Scavenge YGC平均次数=0.1521次/秒
旧的年轻代PS Scavenge YGC平均耗时=35ms