垃圾收集器ParNew&CMS与底层三色标记算法详解
2023-01-29 23:06:50 13 举报
垃圾收集器ParNew&CMS与底层三色标记算法详解
作者其他创作
大纲/内容
<b><font color="#b71c1c">垃圾收集算法</font></b>
分代收集理论
根据对象的存活周期将内存分为几块,jvm中分为新生代和老年代
标记复制算法
讲内存分为大小相等的两块,其中一块的内存满了,复制到另一半内存,回收另一半
标记整理算法(<b><font color="#b71c1c">适合老年代</font></b>)
标记之后不是直接回收对象,而是讲所有<b>存活的对象向一段移动</b>,清除掉边界以外的内存
标记清除算法(<b><font color="#d32f2f">适合年轻代</font></b>)
标记存活的对象,回收未标记对象,是最基础的收集算法
<b><font color="#b71c1c">存在问题</font></b>
效率问题:标记的对象太多,效率不高
空间问题 :标记后会产生大量<font color="#b71c1c"><b>不连续的碎片</b></font>
垃圾收集器
没有万能的垃圾收集器,根据应用场景选择适合自己的垃圾收集
<b style="color: rgb(183, 28, 28);">Serial</b><font color="#000000">收集器</font><font color="#212121">(-</font>XX:+UseSerialGC -XX:+UseSerialOldGC)
串行收集器,<b><font color="#b71c1c">最基本</font></b>的垃圾收集器,进行垃圾收集工作时会<font color="#b71c1c"><b>STW</b></font>
新生代采用<b>复制算法</b>UseSerialGC
老年代采用<b>标记-整理算法</b>UseSerialOld
<b><font color="#b71c1c">优点</font></b>
没有线程交互的开销,可以获得很高的单线程收集效率
作为<b>CMS收集器的后备方案</b>
<b><font color="#b71c1c">Parallel Scavenge</font></b>收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))
是serial的多线程版本,<b>在垃圾收集时使用多线程</b>,减少STW时间,默认线程数和cpu核数相同,可以用参数修改,不推荐
新生代采用复制算法
老年代采用标记-整理算法
<b><font color="#b71c1c">JDK8默认</font></b>的新生代和老年代收集器:Parallel Scavenge收集器和Parallel Old收集器
<b><font color="#b71c1c">ParNew</font></b>收集器(-XX:+UseParNewGC)
新生代采用复制算法
老年代采用标记-整理算法
和Parallel收集器<font color="#b71c1c"><b>区别:</b></font>
可以和CMS收集器配合使用
<b><font color="#b71c1c">CMS(</font></b>Concurrent Mark Sweep<font color="#b71c1c"><b>)</b></font>收集器(-XX:+UseConcMarkSweepGC(old))
第一个实现让垃圾收集线程和用户线程<b><font color="#b71c1c">同时工作</font></b>的垃圾收集器
老年代使用<b><font color="#b71c1c">标记-清除</font></b>算法
<font color="#b71c1c"><b>运作过程:</b></font>
<b>初始标记</b>:
暂停所有的其他线程(STW),并记录下<b><font color="#b71c1c">GC root直接能引用的对象</font></b>,速度很快
<b>并发标记:</b>
从GC root直接关联对象向下遍历对象,可以和用户线程一起并发运行
<b>重新标记</b>:
修正并发标记期间,产生变动的那一部分对象的标记记录,<b style=""><font color="#212121">三色标记中的</font><font color="#b71c1c">增量更新算法</font></b>
<b><font color="#212121">并发清理</font></b>:
开启用户线程,同时开始GC清理没有被标记的区域
<b>并发重置</b>:
重置本次GC中标记的数据
CMS优点:
并发收集,低停顿
CMS缺点:
对CPU资源敏感(会和服务抢资源)
解决方案: 无
无法处理<b><font color="#b71c1c">浮动垃圾</font></b>(在<b>并发标记和并发清理</b>阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了);
解决方案:无
标记-清除算法会产生大量的碎片
解决方案:然通过参数-XX:+UseCMSCompactAtFullCollection可以让jvm在执行完标记清除后再做整理
执行过程中可能一边回收,一边再次触发full gc, 也就是"<b>concurrentmode failure</b>",此时会进入<b>stop the world</b>,<b>用<font color="#ff0000">serial old</font>垃圾收集器来回收</b>
<b><font color="#ff0000">CMS的核心参数:</font></b>
-<b>XX:+UseConcMarkSweepGC:</b>启用cms
-XX:ConcGCThreads:并发的GC线程数
<b>-XX:+UseCMSCompactAtFullCollection</b>:FullGC之后做压缩整理(减少碎片)
-<b>XX:CMSFullGCsBeforeCompaction</b>:多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次
-XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
-XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设<br>定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
-XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引<br>用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在标记阶段
-XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW
-XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;
垃圾收集底层算法
三色标记
在<b>并发标记</b>的过程中,因为标记期间应用线程还在继续跑,<b>对象间的引用可能发生变化</b>,<b><font color="#ff0000">多标和漏标</font></b>的情况就有可能发生。这里我们引入“三色标记”来给大家解释下,把Gcroots可达性分析遍历对象过程中遇到的对象, 按照“<b><font color="#ff0000">是否访问过</font></b>”这个条件标记成以下三种颜色:
<b>黑色</b>
表示对象已经被垃圾收集器访问过, 且这个对象的所有引用都已经扫描过
<b>灰色</b>
表示对象已经被垃圾收集器访问过, 但这个对象上<b>至少存在一个</b>引用还没有被扫描过。
<b>白色</b>
表示对象<b><font color="#ff0000">尚未</font></b>被垃圾收集器访问过
<b>多标会产生浮动垃圾</b>
解决方案:
针对并发标记(还有并发清理)开始后产生的新对象,通常的做法是直接全部当成黑色
<b>漏标会导致被引用的对象被当成垃圾误删除</b>
解决方案:都是虚拟机通过<b><font color="#b71c1c">写屏障</font></b>来实现
<b>增量更新(IncrementalUpdate)<font color="#b71c1c">CMS用</font> 引用赋值操作前</b>
黑色对象一旦新插入了指向白色对象的引用之后, 它就变回灰色对象了,重新标记时会扫描新增的引用
<b>原始快照(Snapshot At The Beginning,SATB)<font color="#b71c1c">G1用 </font>引用赋值操作后</b>
当灰色对象要删除指向白色对象的引用关系时, 就将这个要删除的引用记录下来,并发扫描后将白色对象直接标记为黑色对象
<b><font color="#b71c1c">为什么G1用SATB?CMS用增量更新?</font></b>
SATB相对增量更新效率会高(当然SATB可能造成更多的浮动垃圾),因为不需要在重新标记阶段再次深度扫描<br>被删除引用对象,而CMS对增量引用的根对象会做深度扫描,G1因为很多对象都位于不同的region,CMS就一块老年代区域,重新深度扫描对象的话G1的代价会比CMS高,所以G1选择SATB不深度扫描对象,只是简单标记,等到下一轮GC再深度扫描。
0 条评论
下一页