本文主要讲解“G1垃圾收集器和CMS有什么区别”。感兴趣的朋友不妨看看。本文介绍的方法简单、快速、实用。让边肖带你学习“G1垃圾收集器和CMS有什么区别”!
00-1010 G1之前的垃圾收集器主要将堆区分为伊甸园区、旧区和幸存者区。对于伊甸园来说,幸存者被称为回收过程中的“年轻一代的垃圾收集”。而年轻一代和老一代分别是连续的记忆空间。G1将堆分成几个区域,区域的大小可以通过G1HeapRegionSize参数来设置,这个参数必须是2的幂,范围允许是1Mb到32Mb。JVM将根据堆内存的初始值和最大值的平均值计算分区大小,平均堆大小将被划分为大约2000个区域。一旦设置了分区大小,启动后就不会改变。
伊甸园地区(年轻一代-伊甸园地区)
幸存者区域(年轻一代-幸存者区域)
旧区域(老年)
巨大的区域(巨大的物体区域)。
空闲区域(未分配的区域,也称为可用分区)。
1)G1仍采用世代循环,但不同世代之间的记忆不一定是连续的,不同世代的Region占用数量也不一定是固定的(不建议通过相关选项明确设置年轻世代的规模。将覆盖暂停时间目标)。年轻一代中伊甸园和幸存者的数量会随着每次GC而变化。
2)分区不固定属于哪一代,所以比如一个ygc之后,原来的Eden分区会变成一个空闲的可用分区,然后可能用来分配巨型对象,变成H区。
3)G1 3)中的巨型物体是指占据区域50%以上容量的物体。这个巨大的区域专门用来存放巨型物品。如果一个巨大的物体放不下一个H区域,就会被几个连续的H分区存储。因为巨型对象的转移会影响GC效率,当在并发标记阶段发现巨型对象不再存活时,会直接回收。Ygc在某些情况下也会回收巨型物体。
4)分区可以有效利用内存空间,因为整个集合使用“标记-排序”,Region基于“复制”算法。在GC之后,幸存的对象将被复制到可用的分区(未分配的分区),因此不会有空间碎片。
5)像CMS一样,G1将基于堆大小的计算重新调整(增加)堆空间,例如在fullgc中。但是,与fullgc相比,当无法分配对象或巨大对象无法获得连续分区来分配空间时,G1 GC会尝试扩展堆空间以获得更多可用分区。原则上,G1会计算执行GC的时间,并尽最大努力减少花费在GC上的时间(包括YGC和MIX GC)。如果可能,G1将不断扩展堆空间,以满足对象分配和传输的需要。
6)因为G1提供了“可预测的暂停时间”,这也是一个基于G1的启发式算法,所以G1会估计年轻一代需要多少分区,需要回收多少分区。younggc触发的机会是伊甸园分区数量达到上限的时候。一个年轻人将回收所有伊甸园和幸存者区域。幸存的物体将被转移到另一个新的幸存者区域或旧的区域。如果传输的目标分区已满,可用区域将被标记为S或O区域。
1. Region分区
TLAB(Thread Local Allocation Buffer)本地线程缓冲区
默认情况下,G1 GC将启用Tlab优化。它的作用是在并发的情况下,基于CAS的独占变异器线程可以优先分配一个内存区域(属于Java堆的Eden)中的对象,但由于是Java线程的独占内存区域,没有锁竞争,分配速度更快,每个Tlab都独占一个线程。如果判断要分配的对象是巨型对象,则不使用TLAB。
PLAB(Promotion Local Allocation Buffer) 晋升本地分配缓冲区
在younggc中,对象将把Eden区域中所有幸存的对象转移(复制)到s区域分区。还将把s区对象推广到老年。这个决定晋升的阈值可以由MaxTenuringThreshold设置。推广过程,无论是到S还是O,都是在GC线程的PLAB进行的。每个GC线程都有一个PLAB。
Collection Sets(CSets)待收集集合
要在气相色谱中回收的区域的集合。每一代的区域可以存储在CSet中。CSet中幸存的对象将在gc中移动(复制)。GC CSet中的区域将成为可用分区。
Remembered Sets(RSets)已记忆集合
记忆集存在于每个分区中,每个分区只有一个RSet。它存储其他分区中的对象对该分区中的对象的引用,这是一种入点结构。Ygc扫描一下。
old区对象对于本young区的引用,不需要扫描所有old区。mixed gc时,扫描Old区的RSet中,其他old区对于本old分区的引用,一样不用扫描所有的old区。提高了GC效率。因为每次GC都会扫描所有young区对象,所以RSet只有在扫描old引用young,old引用old时会被使用。
Card Table 卡表
将Java堆划分为相等大小的一个个区域,这个小的区域(一般size在128-512字节)被当做Card,而Card Table维护着所有的Card。Card Table的结构是一个字节数组,Card Table用单字节的信息映射着一个Card。当Card中存储了对象时,称为这个Card被脏化了(dirty card)。 对于一些热点Card会存放到Hot card cache。同Card Table一样,Hot card cache也是全局的结构。
3. G1与CMS的对比
3.1 CMS处理过程
CMS收集器仅作用于老年代的收集,是基于标记-清除算法的,它的运作过程分为4个步骤:
初始标记(CMS initial mark)独占CPU(STW),仅标记GCroots能直接关联的对象
并发标记(CMS concurrent mark)可以和用户线程并行执行,标记所有可达对象
重新标记(CMS remark)独占CPU(STW),对并发标记阶段用户线程运行产生的垃圾对象进行标记修正
并发清除(CMS concurrent sweep)可以和用户线程并行执行,清理垃圾
其中,初始标记、重新标记这两个步骤仍然需要Stop-the-world。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始阶段稍长一些,但远比并发标记的时间短。
3.2 CMS 优点
并发收集、低停顿
3.3 CMS缺点
1)对CPU非常敏感:在并发阶段虽然不会导致用户线程停顿,但是会因为占用了一部分线程使应用程序变慢
2)无法处理浮动垃圾:在最后一步并发清理过程中,用户线程执行也会产生垃圾,但是这部分垃圾是在标记之后,所以只有等到下一次gc的时候清理掉,这部分垃圾叫浮动垃圾
3)CMS使用“标记-清理”法会产生大量的空间碎片:当碎片过多,将会给大对象空间的分配带来问题,会出现老年代还有很大的空间但无法找到足够大的连续空间来分配当前对象,不得不提前触发一次FullGC,为了解决这个问题CMS提供了一个开关参数(-XX:+UseCMSCompactAtFullCollection默认开启),用于在FullGC完成之后进行一次碎片整理,但是内存整理的过程是无法并发的,会导致停顿时间变长
3.4 G1 YoungGC
年轻代垃圾回收只会回收Eden区和Survivor区。YGC时,首先G1停止应用程序的执行(Stop-The-World),G1创建回收集(Collection Set),回收集是指需要被回收的内存分段的集合,年轻代回收过程的回收集包含年轻代Eden区和Survivor区所有的内存分段。
1)第一阶段,扫描根。根是指static变量指向的对象,正在执行的方法调用链条上的局部变量等。跟引用连同RSet记录的外部引用作为扫描存活对象的入口。
2)第二阶段,更新RSet。处理dirty card queue中的card,更新RSet。此阶段完成后,RSet可以准确的反映老年代对所在的内存分段中对象的引用。
3)第三阶段,处理RSet。识别被老年代对象指向的Eden中的对象,这些被指向的Eden中的对象被认为是存活的对象。
4)第四阶段,复制对象。此阶段,对象树被遍历,Eden区内存段中存活的对象会被复制到Survivor区中空的内存分段,Survivor区内存段中存活的对象如果年龄未达阈值,年龄会加1,达到阈值会被复制到Old区中空的内存分段。如果Survivor空间不够,Eden空间的部分数据会直接晋升到老年代空间。
5)第五阶段,处理引用。处理Soft,Weak,Phantom,Final,JNI Weak 等引用。最终Eden空间的数据为空,GC停止工作,而目标内存中的对象都是连续存储的,没有碎片,所以复制过程可以达到内存整理的效果,减少碎片。
3.5 G1 并发标记
当整个堆大小在jvm堆栈空间中占比达到IHOP阈值-XX:InitiatingHeapOccupancyPercent(默认45%)时,G1就会启动一次混合垃圾收集周期。Mix GC不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区。进行Mix GC之前,会先进行全局并发标记。
1)初始标记(InitingMark):标记GC Roots,会STW,一般会复用YoungGC的暂停时间。初始标记会设置好所有分区的NTAMS值。
2)根分区扫描(RootRegionScan):根据初始标记阶段确定的GC根元素,扫描这些元素所在region,获取对老年代的引用,并标记被引用的对象。 该阶段与应用线程并发执行,也就是说没有STW停顿,必须在下一次年轻代GC开始之前完成。
3)并发标记(ConcurrentMark):遍历整个堆,查找所有可达的存活对象。若发现区域对象中的所有对象都是垃圾,那这个区域会被立即回收。 此阶段与应用线程并发执行, 也允许被年轻代GC打断。
4)最终标记(Remark):此阶段有一次STW暂停,以完成标记周期。 G1会清空SATB缓冲区,跟踪未访问到的存活对象,并进行引用处理。
5)清除阶段(Clean UP): 这是最后的子阶段,G1在执行统计和清理RSet时会有一次STW停顿。 在统计过程中,会把完全空闲的region标记出来,也会标记出适合于进行混合模式GC的候选region。 清理阶段有一部分是并发执行的,比如在重置空闲region并将其加入空闲列表时。
清除阶段之后,还会对存活对象进行转移(复制算法),转移到其他可用分区,所以当前的分区就变成了新的可用分区。复制转移主要是为了解决分区内的碎片问题。
3.6 G1MixedGC
1)并发标记结束以后,老年代中百分百为垃圾的内存分段被回收了,部分为垃圾的内存分段被计算了出来。默认情况下,这些老年代的内存分段会分8次(可以通过-XX:G1MixedGCCountTarget设置)被回收。
2)混合回收的回收集(Collection Set)包括八分之一的老年代内存分段,Eden区内存分段,Survivor区内存分段。混合回收的算法和年轻代回收的算法完全一样,只是回收集多了老年代的内存分段。具体过程请参考年轻代回收过程。
3)由于老年代中的内存分段默认分8次回收,G1会优先回收垃圾多的内存分段。垃圾占内存分段比例越高,越会被先回收。并且有一个阈值会决定内存分段是否被回收。-XX:G1MixedGCLiveThresholdPercent,默认为65%,意思是垃圾占内存分段比例要达到65%才会被回收。如果垃圾占比太低,意味着存活的对象占比高,在复制的时候会花费更多的时间。
4)混合回收并不一定要进行8次。有一个阈值-XX:G1HeapWastePercent,默认值为10%,意思是允许整个堆内存中有10%的空间被浪费,意味着如果发现可以回收的垃圾占堆内存的比例低于10%,则不再进行混合回收。因为GC会花费很多的时间但是回收到的内存却很少。
3.7 G1特点
1)并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-the-world停顿的时间,部分其他收集器原来需要停顿Java线程执行的GC操作,G1收集器仍然可以通过并发的方式让Java程序继续运行。
2)分代收集
3)空间整合:与CMS的标记-清除算法不同,G1从整体来看是基于标记-整理算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的。但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。
4)可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用这明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
到此,相信大家对“G1垃圾回收器与CMS的区别有哪些”有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/37467.html