什么时候触发新生代和老年代的混合垃圾回收
G1有一个参数-XX:InitiatingHeapOccupancyPercent,他的默认是45%,他的含义就是老年代到了45%的时候,就会进行混合回收,比如有2048个region,其中老年代占据了有1000个region,就会混合回收.
G1垃圾回收的具体过程
初始化标记
初始化标记先停止新运行,然后对线程内存中的局部变量代表的GC Roots,以及方法区中的静态变量代表的GC Roots,进行扫描,标记他们的直接引用
并发标记
并发标记系统正常运行,并发标记就是从GC Roots开始最终所有存活对象,由于系统正常运行,此时会记录修改的对象
最终标记
最终标记,系统就会停止运行,根据并发标记阶段记录的那些向修改,最终标记一下那些对象存活,那些垃圾对象
混合回收
最后一个阶段,计算老年代中每个region中存活的对象数量,存活对象占比,还有执行垃圾回收预期性能和效率,此时系统停止运行,全力进行垃圾回收,此时会选择部分region进行回收,因为必须让垃圾回收停顿时间在控制范围内,
例如,老年代有1000个region都满了,预期目标就是200ms的停顿时间,通过计算最大能回收800个region,这样把停顿时间控制在范围内,
说明一点就是老年代达到45%(-XX:InitiatingHeapOccupancyPercent进行设置,默认是45%)的时候,触发的是混合回收,就是说回收老年代和年轻代,以及大对象,
G1垃圾回收重要参数
我们知道G1垃圾回收有几个阶段,最后一个阶段就是混合回收,其实停止所有程序运行,然后在进行全力回收,但是我要注意的是,这个回收可能要反复好几次,反复几次是有-XX:G1MixedGCCountTarget参数控制,默认值8,就会每次回收会反复回收8次,比如我们预期要会搜160个region,因此每次会回收20个region,反复8次就可以回收160region,这样所得好处就是,是系统有间隙执行系统,不会一直停顿,
另外一个参数-XX:G1HeapWastePercent,默认是5%,由于我们的G1回收是基于复制算法进行的,因此他会把存活的而对象复制到另外一个region,然后一次性清理有垃圾的region,而这个参数的作用就是混合回收的空闲region达到堆内存的5%的时候,就会停止混合回收,意味回收就结束了,
另外一个参数“-XX:G1MixedGCLiveThresholdPercent 默认是85%,作用就是要回收的region存活的对象必须低于85%才会被回收,不然存活对象大于85%,回收他成本很高
回收失败的情况
当然我们知道,复制算法,是要把存活的对象复制到另外一个region,但是如果没有空闲的region可以存放存活的对象,就会触发失败,一旦失败,就会停止系统程序,然后采用单线程进行标记,清理,压缩整理,空闲一批region,切记这个过程很慢
G1垃圾回收中如何触发新生代GC
背景是堆内存是4G,一共2048个region,每个region是2M,其中新生代开始占5%,也就是100region,新生代200M,假设系统每秒产生3M,则一分钟新生代Eden几乎就满了,这里我就想起以前我们记得是当Eden满的时候触发GC,
但是G1,不是这样的,因为如果此时回收这200M内存消耗几十毫秒,而我们记得参数-XX:MaxGCPauseMills=200ms,和我们预期的相差太远
而且这样回收回到每分钟就会发生一个新生代GC,频繁的发生GC,是没有这个必要的,因此不如在此时给新生代再分配一些region,然后新生代继续分配对象,最后G1发现回收300m内存需要消耗200ms,那这个时候触发一次新生代GC,是最好的,这个我们要明白G1是很灵活的,他会根据你设定的gc停顿时间给你的新生代不停的分配region,到的一定程度,就会触发新生代GC,保证在预期的时间范围内。大致思想就是上面这些,但是具体还要根据实际情况才知道.
mixed GC如何优化
mixed GC是当老年代占堆内存超过45%的时候就会触发,这mixed gc优化的核心不是参数的优化,是如何让新生代不会进入老年代,所以最重要的还是我们之前说的参数-XX:MaxGCPauseMills参数,如果设置大了,就会新生代达到60%才会触发新生代GC,且存活的对象就会很多,而此时Suvivor放不下对象,就会进入老年代,所以要保证-xx:MaxGCPauseMills参数既要保证新生代不要频繁的gc,还要保证存活的对象有多少,避免存活对象太多进入老年代,频繁发生Mied GC
运行的系统,怎么会卡顿呢
我们的系统正常运行,对象不断的产生,当新生代Eden块要满的时候,就会发生垃圾回收,此时就会把存活的对象放到S1,那些是存活的对象就是GC Roots引用的对象,如类的静态变量和实例变量,被这些对象引用,就是存活的对象,
更重要的是系统此时是对外是停止运行,这个就是Stop the World,就会导致系统卡顿的问题,
新生代gc对系统的卡顿的影响
一般我们新生代进行GC,如果内存足够,几个小时进行新生代GC,高峰期最多也就几分钟一次新生代GC,通常新生代复制算法效率很高,因此一般情况下,只有少数的对象,然后进行回收对系统影响很小
但是如果你的机器是一个32核64G的机器,分给堆内存大概就有几十GC,Eden可能有30-40的内存,
正好的你使用了kafak,elasticserch之类的大数据系统,部署在大内存机器中,每秒几万的请求,就可能导致几十G内存发生频发内存发生,假设1分钟会塞满一次,每次GC,都会消耗几百毫秒,甚至1秒,就会导致你的系统卡顿,这个频繁的卡顿,对用户影响极大
如何解决大内存新生代GC过慢的问题
其实遇到大内存的机器GC,我们肯定就想到了G1垃圾收集器,G1就是为了大内存机器垃圾回收慢的问题场景提供的,
G1预估系统停顿的时候,让垃圾在合理的时间内进行回收,完美的解决了停顿时间过程的问题,同时我们要知道频繁老年代GC的问题
前面我们讲过新生代gc问题不大,问题是频繁的老年代GC问题,我们也讲过进入老年代的几个条件就是,年龄多大,新生代的survivor存活对象太多,无法进入Survivor中,进入老年代,还有就是动态年龄判定规则,suvivor区域几个年龄对象加起来超过Survivor的50%
我们要知道老年代的GC是非常慢的,比如新生代一次GC消耗200ms,对用户影响不大,但是老年代耗费2s,就会导致页面卡顿2s,影响很大的,根本解决问题就是尽可能不让对象进入老年代.