到底是谁在回收 JVM 的垃圾~
来源 | 星尘的一个朋友(ID:rushjava)
开往虚拟机的车已经出发,关注上车
❝虚拟机的垃圾回收器,没有哪一个是绝对好的,只有比较好的。
❞
今天的这篇文章,我要与你分享虚拟机的那些垃圾回收器们。内容不多,可以耐心看完。
垃圾收集器
那些回收 JVM 垃圾的家伙
Serial
关键字:新生代、Stop The World 、标记 - 复制算法、单线程
ParNew
关键字:新生代、Stop The World 、标记 - 复制算法、多线程、「绝配」
Parallel Scavenge
关键字:新生代、Stop The World、标记 - 复制算法、多线程、可控吞吐量【用户线程执行时间 /(用户线程执行时间+GC线程执行时间)】、「自适应策略」
上面这三种 「Serial」 、 「ParNew」 、 「Parallel Scavenge」 都是新生代的垃圾收集器,下面我们来看看老年代的垃圾收集器。
Serial Old
关键字:老年代、Stop The World、标记 - 整理算法、单线程、「替补」
Parallel Old
关键字:老年代、Stop The World、标记 - 整理算法、多线程、CP(组合)
CMS
关键字:老年代、Stop The World、标记 - 清除算法、多线程、短停顿
具体步骤如下:
初始标记:需要 Stop The World 并发标记:与用户线程并发 重新标记:需要 Stop The World 并发清除:与用户线程并发
标记 GC Roots 直接关联的对象(直达),速度较快,停顿时间短。
进行标记追踪,完成全部对象的标记任务。可能出现漏标或错标情况。
修正并发标记阶段的对象标记,因为大部分对象不需要修正,所以执行时间相比并发标记时间短,但是停顿时间要比初始标记停顿时间长。
耗时较长,可以与用户线程共同工作。
CMS 虽然有了一个较短的停顿时间,但是也有一些其他随之而来的问题。
优点
并发执行速度快、停顿时间短。这一点没得说,因为它其中有两个阶段是和用户线程并发。
缺点
「占用线程资源」,因为 CMS 工作有两个阶段是和用户线程并发,所以这里便会抢占用户线程资源。 「浮动垃圾」,清理一次之后还会有清理不掉的对象,需要在下次清理的时候才能够清理到。 「空间利用率低」,因为并发清理的原因,所以不能等到内存完全用完之后再做清理,所以需要当内存使用达到一定阈值(默认值68%,JDK6的时候提高到了 92%)时就开始进行垃圾回收动作,具体数值可以通过参数控制。 「空间碎片」,因为使用标记-清除算法的原因,会导致碎片空间的产生。CMS 的做法是在其不能够满足对象分配任务的时候,FULL GC 的时候,会进行一次空间整理的动作。 对这个整理的动作也是有参数可以进行控制,参数设置情况为,满足几次FullGC之后,进行一次空间整理,默认值为 0 ,即每次 full gc 都会进行一次空间整理。
这一点虽然是缺点,不过 CMS 已经尽力去弥补了,包括这里的 FULL GC 之后的内存空间整理,还有对象分配时 CMS 会在 Free List 申请一块较大的内存空间,然后通过指针碰撞的方式来进行对象分配,尽可能减小空间碎片的产生。
空间碎片问题也是 CMS 不能直接使用指针碰撞的方式来为对象分配内存的原因。
这里的原因是因为并发清除阶段是和用户线程并发,一边清除一边使用,可能会出现一些无法清理掉的新生垃圾,比如清理过程中,程序断开了某个引用,被断开的引用 GC Roots 不可达,所以这个被断开的引用指向的对象变成了浮动垃圾。
这里 JVM 给了风险预案:冻结用户线程,启动 serial old 来进行一次老年代垃圾回收。这也是上面我们说 Serial Old 的时候,提到他的关键字里有 “替补” 的原因。
Garbage First (G1)
关键字:里程碑、JDK 9、区域管理、按需回收、「延迟可控的最高吞吐量」
❝多了解一点,关于 G1 的工作模式,Mixed Mode 的扩展:G1 有纯 GC 模式和分代回收模式,分代模式会分为 Minor GC 和 Mixed GC 两种,这里的模式选择会影响最后的筛选回收阶段的回收集合的内容。这块内容可参考后面留的 R大 的链接
❞
下面我们一起了解一下 G1 工作的具体步骤:
初始标记:Stop The World 并发标记:与用户线程并发 最终标记:Stop The World 筛选回收:Stop The World
标记 GC Roots 直接关联的对象,同时修改 TAMS 指针
标记全部要回收的对象,与用户线程并发,标记完成之后,重新处理 SATB 记录下在并发时有引用变动的对象
处理并发标记阶段 SATB 遗留的引用,同时这个阶段也进行弱引用处理。
这个阶段会更新 Region 的统计数据,对每个 Region 根据其回收价值和成本进行排序,然后根据用户所期望的停顿时间(参数设置)来制定一个回收计划。
再根据这个计划,选择任意 Region 来组成一个回收集(collection set)。将回收集中的 Region(被选中的区域) 中的存活对象复制到空的 Region 中,然后将旧的(选中的) Region 清理掉。
以上过程由多线程并行完成,同时因为移动对象需要暂停用户线程(Stop The World)
❝TAMS:Top at Mark Start Region 中的两个指针名称,他们的作用是将 Region 的一部分空间划分出来给并发回收过程中程序运行产生的新对象使用
SATB:原始快照,还记得之前我们对漏标的解决方案吗?一种是增量更新(CMS 采用的这种方案),另外一个就是原始快照,这里可以翻翻之前的内容。
❞
G1除了并发标记阶段都需要暂停用户线程
G1的理想目标:在延迟可控的情况下达到最大的吞吐量
用户可以通过参数设置所期望的停顿时间,这个时间一般建议设置为 100 ~ 300 ms。
垃圾收集器小结
上面一共说了 7 款垃圾收集器,不过他们的具体使用我觉得有必要了解一下。
按照分工划分
「新生代」Serial、ParNew、Parallel Scavenge
「老年代」Serial Old、Parallel Old、CMS
「内存区域」:G1
按照搭配组合划分
Serial + Serial Old 的单线程组合,适用于资源受限的场景。 ParNew + CMS 这组曾经的王者组合,新生代的多线程并行高性能加上老年代的短暂停顿组合,可以应对大部分场景。 Parallel Scavenge + Serial Old 这是 Paralle Old 没出现的时候的应对组合(不明白为何 JDK 9 的时候没取消这对奇葩组合) Parallel Scavenge + Parallel Old 这对高吞吐量 CP
G1的出现
现状
综上所述,到 JDK 9 之后还剩下建议使用的组合如下
G1 Serial + Serial Old Parallel Scavenge + Serial Old (还是它,我一定要关注它俩啥时候下岗)
所以通过上面的分析,也能看出来 HotSpot 的用意,在 JDK 9 以后就是要将 G1 作为一个全能型的垃圾收集器来发展。
写在最后
https://www.iteye.com/blog/rednaxelafx-362738
往 期 推 荐 1、阿里云盘正式公测!免费领1年云盘扩容码,速来,先到先得! 2、牛逼!IntelliJ IDEA居然支持视频聊天了~速来尝鲜!快来冲一波 3、如何设置最近爆火的微信开关头像?想学啊,我教你啊~ 4、知名国产网盘翻车?清空免费用户文件后,又开始清理付费用户资源 5、Chrome新功能曝光:你访问的敏感网站可以自动隐藏起来 6、万万没想到,“红孩儿”竟然做了程序员,还是CTO! 7、徒手撸一个Spring Boot中的starter,解密自动化配置,超级棒!
点分享
点收藏
点点赞
点在看