G1简介

1,904 阅读7分钟

大纲

1、G1的内存模型

            region 个数、大小 新生代的初始占比,最大占比

2、eden 、survivor、old 区域的内存占比? 

3、数据何时进入老年代

            年龄 

             动态年龄判断 

             大对象如何存储

4、垃圾回收

            选取哪些内容进行回收

            垃圾回收时间设置

            新生代垃圾回收算法,与之前才的复制算法的区别

            老年代垃圾回收算法?老年代反复回收多次?老年代垃圾回收何时出发?何时停止?

            老年代哪些region会被回收?

            老年代垃圾回收的担保机制?

1. 为什么引入G1,G1的使用场景

       无论是新生代垃圾回收,还是老年代垃圾回收,都会或多或少产生“Stop the World”现象,对系统的运行是有一定影响的。所以其实之后对垃圾回收器的优化,都是朝着减少“Stop the World”的目标去做的。 在这个基础之上,G1垃圾回收器就应运而生了,他可以提供比“ParNew + CMS”组合更好的垃圾回收的性能。

2、G1的内存模型


G1把Java堆内存拆分为多个大小相等的Region

每个region的大小等于(堆内存大小/2048)。比如说堆大小是4G,那么就是4096MB,此时除以2048个Region,每个Region的大小就是2MB。大概就是这样子来决定Region的数 量和大小的

新生代初始大小:刚开始的时候,默认新生代对堆内存的占比是5%,也就是占据200MB左右的内存,对应大概是100个Region,这个是可以通过“- XX:G1NewSizePercent”来设置新生代初始占比的

新生代最大值:在系统运行中,JVM其实会不停的给新生代增加更多的Region,但是最多新生代的占比不会超过60%,可以通过“- XX:G1MaxNewSizePercent”

一旦Region进行了垃圾回收,此时新生代的Region数量还会减少,这些其实都是动态的

eden和survivor:参数和比例与之前保持不变。“-XX:SurvivorRatio=8”,这里还是可以区分出来属于新生代的Region里哪些属 于Eden,哪些属于Survivor。比如新生代之前说刚开始初始的时候,有100个Region,那么可能80个Region就是Eden,两个Survivor各自占10个Region。随着对象不停的在新生代里分配,属于新生代的Region会不断增加,Eden和Survivor对应的Region也会不断增加。

新生代GC触发时机:

          1、新生代达到了设定的占据堆内存的最大大小60%;

          2、G1执行GC的时间到达系统停顿多长时间。可以通过“-XX:MaxGCPauseMills”参数来设定,默认值是200ms。

老年代最大大小:老年代最多可以占据40%的Region

3、数据何时进入老年代

  1. 年龄 :对象在新生代躲过了很多次的垃圾回收,达到了一定的年龄了,“-XX:MaxTenuringThreshold”参数可以设置这个年龄,他就 会进入老年代 
  2. 动态年龄判定规则,如果一旦发现某次新生代GC过后,存活对象超过了Survivor的50%


大对象:G1提供了专门的Region来存放大对象,而没有将大对象进入老年代的Region中。在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%,比如按照上面算的,每个Region是2MB,只要一个大对 象超过了1MB,就会被放入大对象专门的Region中 而且一个大对象如果太大,可能会横跨多个Region来存放。

大对象何时回收:新生代、老年代在回收的时候,会顺带带着大对象Region一起回收

4、垃圾回收

region回收优先选取策略:

       触发垃圾回收的时候,可以根据设定的预期系统停顿时间,来选择最少回收时间最多回收对象的Region进行垃圾回收,保证GC 对系统停顿的影响在可控范围内,同时还能尽可能回收最多的对象。


新生代GC触发时机:

1、新生代达到了设定的占据堆内存的最大大小60%;

2、G1执行GC的时间到达系统停顿多长时间。可以通过“-XX:MaxGCPauseMills”参数来设定,默认值是200ms。

新生代+老年代的混合垃圾回收 触发时机:

       如果老年代占据了堆内存的45%的Region的时候,此时就会尝试触发一个新生代+老年代一起回收的混合回收阶段。

参数:“-XX:InitiatingHeapOccupancyPercent”,他的默认值是45%


复制算法的区别:

新生代和老年代都采取了复制算法

新生代的垃圾回收还是采用了复制算法,只不过会考虑预设GC停顿时间,保证垃圾回收的停顿时间不能超过预设时间,因此会挑选一 些Region来进行垃圾回收。

混合垃圾回收详解:

  • 初始标记:需要进入“Stop the World”的,仅仅只是标记一下GC Roots直接能引用的对象, 这个过程速度是很快的。
  • 并发标记:这个阶段会允许系统程序的运行,同时进行GC Roots追踪,从GC Roots开始追踪所有的存活对象,很耗时的,因为要追踪全部的存活对象。JVM会对并发标记阶段对对象做出的一些修改记录起来,比如说哪个对象被新建了,哪个对象失去了引用。
  • 最终标记:会进入“Stop the World”,系统程序是禁止运行的,但是会根据并发标记 阶段记录的 那些对象修改,最终标记一下有哪些存活对象,有哪些是垃圾对象
  • 混合回收:这个阶段会计算老年代中每个Region中的存活对象数量,存活对象的占比,还有执行垃圾回 收的预期性能和效率。接着会停止系统程序,然后全力以赴尽快进行垃圾回收,此时会选择部分Region进行回收,因为必须让垃圾回收的停顿时间控制在我 们指定的范围内。

G1垃圾回收器参数

  • “-XX:G1MixedGCCountTarget”:

就是在一次混合回收的过程中,最后一个阶段执行几次混合 回收,默认值是8次。

意味着最后一个阶段,先停止系统运行,混合回收一些Region,再恢复系统运行,接着再次禁止系统运行,混合回收一些Region,反 复8次。

为什么要反复回收多次呢? 因为你停止系统一会儿,回收掉一些Region,再让系统运行一会儿,然后再次停止系统一会儿,再次回收掉一些Region,这样可以尽 可能让系统不要停顿时间过长,可以在多次回收的间隙,也运行一下。减低用户对GC停顿的感知


  • “-XX:G1HeapWastePercent”:堆浪费百分比,默认值是5%

当G1发现可被回收的空间小于5%时,就不会再进行混合收集,也就是会结束当前的混合收集周期


  • “-XX:G1MixedGCLiveThresholdPercent”:默认值是85%,意思就是确定要回收的Region的时候,必须是存 活对象低于85%的Region才可以进行回收
为什么呢?因为要把85%的对象都拷贝到别的Region,这个成本是很高的

混合回收失败时的Full GC

      如果在进行Mixed回收的时候,无论是年轻代还是老年代都基于复制算法进行回收,都要把各个Region的存活对象拷贝到别的Region 里去 ,此时万一出现拷贝的过程中发现没有空闲Region可以承载自己的存活对象了,就会触发 一次失败。

       一旦失败,立马就会切换为停止系统程序,然后采用单线程进行标记、清理和压缩整理,空闲出来一批Region,这个过程是极慢极慢 的。 


附带一篇文章:

垃圾优先型垃圾回收器调优

www.oracle.com/cn/technica…