$java$奇异的G1——Java全新垃圾回收机制

 

奇异的G1——Java全新垃圾回收机制

G1全称是GarbageFirstGarbageCollector,使用G1的目的是简化性能优化的复杂性。例如,G1的主要输入参数是初始化和 Java堆大小、 GC中断时间。

G1GC由YoungGeneration和OldGeneration组成。G1将Java堆空间分割成了若干个Region,即年轻代/老年代是一系列Region的集合,这就意味着在分配空间时不需要一个连续的内存区间,即不需要在JVM启动时决定哪些Region属于老年代,哪些属于年轻代。由于随着时间推移,年轻代Region被回收后,又会变成可用状态(后面会说到的UnusedRegion或AvailableRegion)了。

G1年轻代搜集器是并行Stop-the-world搜集器,和其他的HotSpotGC一样,当一个年轻代GC产生时,全部年轻代被回收。G1的老年代搜集器有所不同,它在老年代不需要全部老年代回收,只有一部分Region被调用。

G1GC的年轻代由EdenRegion和SurvivorRegion组成。当一个JVM分配EdenRegion失败后就触发一个年轻代回收,这意味着Eden区间满了。然后GC开始释放空间, 个年轻代搜集器会移动所有的存储对象从EdenRegion到SurvivorRegion,这就是“CopytoSurvivor”进程。

清单1所示是年轻代的回收GC输出日志,在这个日志里面,请见 一行,年轻代新的大小是(NewEden)+32(NewSurvivor)=MB

清单1G1回收年轻代

当一个Java堆空间瓶颈点超过后,即堆空间耗尽,这时候G1初始化老年代搜集器,这个Initial-Mark阶段是一个并行Stop-the-World的,它的大小和老年代和全部Java堆大小有关。

Initial-Mark阶段和下一个YoungGC一起履行,一旦Initial-Mark完成,一个多线程并行的Marking阶段开始去标记老年代所有存活的对象。当并行Marking阶段完成,一个并行的Stop-the-World的阶段开始去标记所有的对象(由于和Marking阶段并行存在,应用程序多线程程序可能会丢失数据)。Remark节点结束后,G1有老年代所有Region的完全的标记信息,如果这时候老年代没有任务存活对象,那末下一个阶段Cleanup阶段就不需要额外的GC工作了。

上面的描写是关于老年代搜集器的流程描写,扼要说明就是Initial-Mark-ConcurrentRootRegionscanning-ConcurrentMarking-Remarking-Cleanup。

在Cleanup阶段,如果G1GC发现一个可回收的Region,不用等到下一个GC停顿可以直接回收这些Region并且加入到空闲Region的LinkedList链表。

CMS、Parallel、SerialGC都需要通过FullGC去紧缩老年代并在这个进程中扫描全部老年代。

由于G1的操作以Region为基础,因此它适用于大Java堆。即使Java堆很大,大量的GC工作可以被限制在小型Region集合里面。G1允许用户指定停顿时间目标,G1通过自适应的堆大小来满足这个目标。

※G1GC深度原理G1把全部Java堆划分为若干个区间(Regions)。每一个Region大小为2的倍数,范围在1MB-32MB之间,可能为1,2,4,8,16,32MB。所有的Region有一样的大小,JVM生命周期内不会改变。例如-Xmx16g–Xms16g,设置16GB的堆大小,个Regions,则每一个Region=16GB/=8MB。如果堆大小很大,而每一个Region的大小很小,则Region数量可能会超过个。一样地,很小的堆大小会致使Region数量很少。

Region类型

G1对Region的几个定义:

AvailableRegion=可用的空闲Region

EdenRegion=年轻代Eden空间

SuivivorRegion=年轻代Survivor空间

所有Eden和Survivor的集合=全部年轻代

HumongousRegion=大对象Region

HumongousRegion

大对象是指占用大小超过一个Region50%空间的对象,这个大小包括了Java对象头。对象头大小在32位和64位HotSpotVM之间有差异,可以使用JavaObjectLayout工具肯定头大小,简称JOL。

当大对象开始进入排队时,G1会调动几个连续的有效Region寄存它。 个Region叫做“大对象开始”Region,其他Regions叫“大对象延续”Regions。如果没有连续的可用Regions,G1会做一个Javaheap的fullgc去紧缩对象。大对象区间属于老年代的一部分,它只包括一个对象,这个属性允许G1搜集一个大对象区间当并行Marking阶段发现没有对象存活时。当这个条件触发,所有包括大对象的区间都可以立即被回收申明。

对G1来讲,一个潜伏的挑战是短生命周期的大对象可能不会被申明直到它们变成没有被援用。JDK8U45申明了一个方法在年轻代回收大对象Region。

前面说过,G1Region包括YoungRegion、OldRegion、HumougousRegion、FreeRegion。每一个搜集器单元逾越一个年轻和老年Regions。一个大对象逾越两个搜集器单元,所以大对象Region是一个连续的Region,如图1所示。

图1Region逾越分布图1

我们再来看图2,大对象1逾越两个连续区间。 个连续Region被标记为StartHumongous,接下来连续的Region叫做ContinuesHumongous。大对象2逾越三个连续的堆Regions,大对象3逾越了一个Region。

图2Region逾越分布图2

RSet

基于老年代的搜集器采取Heap里不同区域辨别/隔离对象,这些不同的区域里面的对象对应了不同年代。这样年代搜集器可以集中精力在最近分配的对象上,由于它们会发现一些对象不久会死亡。这些年代在堆里可以被分别搜集,这样不用扫描全部Heap,可以节省时间和减小响应时间,并且存活时间长的对象不用来回复制,减少了拷贝和援用更新开消。

为了方便搜集器的独立性,许多GC保持了每一个年代的RSet。每个RSet是一个数据结构,它保护并跟踪搜集器单元的内部援用,如G1GC的Region一样,减少了扫描全部Heap堆获得信息的耗时。当G1GC履行了一个Stop-the-world搜集(年轻代或混合代),它可以通过CSet扫描Region的RSets。一旦存活对象被移除,它们的援用立即被更新。

图3RSet布局图示例

从上面的图3,我们可以看见一个年轻代Region(RegionX)和两个老年代Region(RegionY和RegionZ)。RegionX有一个从RegionZ来的援用。这个援用被标记在了RegionX的RSet里面。我们也观察到RegionZ有两个援用,一个来自于RegionX,另一个来自于RegionY。RegionZ的RSet需要标记从RegionY过来的援用,但是不需要去记住从RegionX来的援用,由于年轻代是全局被搜集的。对RegionY,终究我们可以看到从RegionX来的援用,并没有在RegionY的RSet里记录援用。

MixedGC事件

随着很多对象被提升到老年代,和大对象进入大对象区间,全部Java堆区占有率上升。为了避免Java堆空间溢出,JVM进程需要去初始化一个GC(不但包括年轻代Regions,也包括增加老年代Region到混合搜集器)。在混合GC事件里,所有的年轻代Regions会被搜集,同时一部分老年代Region也会被搜集。

清单2G1老年代回

FullGarbageCollections

G1内部,前面提到的混合GC是非常重要的释放内存机制,它避免了G1出现Region没有可用的情况,否则就会触发FullGC事件。

CMS、Parallel、SerialGC都需要通过FullGC去紧缩老年代并在这个进程中扫描全部老年代。G1的FullGC算法和SerialGC搜集器完全一致。当一个FullGC产生时,全部Java堆履行一个完全的紧缩,这样确保了 的空余内存可用。G1的FullGC是一个单线程,它可能引发一个长时间的停顿时间,G1的设计目标是减少FullGC,满足运用性能目标。

※G1GC经常使用参数我在这里所罗列的参数的默认值都是基于JDK8u45,所以可能后续的JDK版本会有些值不一样,这个读者可以直接通过JDK的官方帮助文档获得 默认值信息。

-XX:+UseG1GC:启用G1GC。JDK7和JDK8要求必须显示申请启动G1GC,JDK可能会设置G1GC为默许GC选项,也有可能会退到初期的ParallelGC,这个也请
































北京治疗白癜风的好医院
白癜风医院有哪些
  转载请注明原文网址:http://www.do-mallexpress.com/qygmr/447.html
  • 上一篇文章:
  • 下一篇文章: