G1 Young GC时的to-space


本文目的:利用线上故障的GC日志,来了解G1的GC过程。

    最近在hbase集群一台机器的RegionServer突然挂掉了,经观察日志发现是Young GC时发生了to-space。

这里首先简单介绍一下G1和to-space。

    G1是JAVA最新的一款垃圾回收器,它主要看重的是GC的停顿时间,用户可以设置一个允许的停顿时间,G1力图保证每次GC都在这个时间范围内。

to-space:了解CMS的应该都清楚young GC的过程。简单的来说,JAVA将内存分为新生代和老生代,young GC只发生在新生代中。

Young GC:新生代的内存可以分为2部分:eden区,survivor区。新object在eden中创建,然后young GC发生时,G1将eden中存活的对象拷贝到survivor中,如果此时存活的对象较多,survivor装不下时,此时会发生to-space错误。

G1垃圾回收的简单介绍:

    之前查了一些资料,都是按照官网的介绍抄下来的,看了之后还是一知半解,这里我以自己当时困惑为出发点来说一下G1的简单过程:

G1的主要有3中行为:young GC, Mixed GC, 和循环标记(marking cycle)

G1将内存分为多个region,可以理解就是将内存分为很多块,目标是尽量将region个数维护在2048个左右,每个region的大小建议在1-32M之间,这个一般是系统自己调整。

这些region有些属于新生代,有些属于老生代。可以看一下一个大致的图解:

    首先说一下young GC,young GC会stop the world,也就意味着这个阶段,所有的java进程都会挂起。G1的新生代大小是会不断扩张的,需要新的内存时,就会分配,然后在young GC的时候进行回收,回收时一般会将age比较大的object放入老生代中。

    young GC只回收新生代的内存,这时候有人可能会问了,那老生代的啥时候回收呢?这个时候就要说一下循环标记(init-marking)和mixed GC了,一般在内存使用到一定程度之后(InitiatingHeapOccupancyPercent=45默认为45%),此时G1会启动循环标记.

    由于循环标记(init-marking)有很多步骤,很多博客都有介绍,这里就不详细写了,大致说一下它的主要作用:就是标记出完全没有被占用的region和老生代中可以被回收的region。因为内存占用到达了阈值,所以要对老生代进行回收,此时就要挑出来最应该被回收的region。

    然后轮到mixed GC了,mixed GC可以看作是young GC的加强版,它不仅按照young GC的方式回收新生代,同时也将循环标记过程中,标记的老生代中的一部分region一并回收(注意这里是一部分,为什么是一部分?应该是G1为了保证响应时间,所以每次只回收一定量的region,而且官网也写了mixed GC可能会连续执行多次,在多次执行之后,老保证标记的region完全被回收),这样执行多次mixed GC,来清除无用的object。

    下面我们来观察一个正常的GC日志来了解一下G1,如图:

这是youngGC的:

    可以看到括号里面有(young),代表这是young gc,Eden:3072M->0.0B,survivors:192M--》384M,Heap:22.6G(64.0G)->19.7G(64.0G):代表新生代中的eden区回收到了survivor区中,存活的对象有384M。总共使用的内存由22G变成了19.7G。可以理解为此次回收了2G多的内存。

    图中的heap before gc和heap after gc可以看出回收前有102个young region,回收后有12个young region。可以看到region size32768K(32M),大约算一下,正好可以得出有2G多的差距,正好和上面的heap变化对应上。  

下面是marking的日志:

    图中可以看到heap在28G左右的时候发生了marking(marking都是顺带的在youngGC中进行的),跟默认的配比45%也正好对应。日志比正常的youngGC多了一些,多的那些就是官网写的marking的几个步骤,具体干了什么可以参看官网。

    之后就开始要进行mixed GC了(不过我这边的服务器观察的是init marking之后,往往还会有个young GC,然后在执行mixed GC,具体为啥没搞清楚):

    mixed GC:

    mixedGC的日志和young GC的日志挺像的,也没啥说的,就是回收的时候顺带把老生代中的一些region也回收了。

 

线上问题:

线上的hbase的regionserver突然挂掉了,观察日志发现是GC时间过长,我们保留了当时的GC日志,下面是故障时间点的截图:

    图中可以看出,24号18点到25号18点之间,整整一天,这个regionserver是没有进行任何GC的,然后过了一整天,直接执行了young GC,但是图中可以看到一些问题:

    heap before的young大小是大约4G,但是eden却显示回收了36.4G内存,一般情况下这两个数值应该是匹配的,这里差距很大。

    heap:54.2G回收之后还是54.2G,eden虽然空了,但是内存占用却没有变,个人猜测:有可能是eden中的对象并没有回收,而是全部挪入了老生代,导致时间开销很大,达到了43s。

    疑问:而且还有一点非常奇怪的是,回收前和回收后的young region个数是一模一样的,再加上之前说的eden和young大小不匹配,这里的显示也是有问题的。

这个young GC之后,后面便开始了init-marking过程。但是此时已经暂停了太长的时间,导致hbase和hadoop的守护进程都认为机器已死,已经停止了服务。

 

分析上面的问题,得出以下猜测:

可能的原因:长时间没有GC,而且eden中的数据并没有回收,而是挪入了老生代,导致了长时间的暂停。

观察其他业务的日志,正常情况下早就应该进行young GC的,这个集群由于目前没有上线,访问量很小,经常流量为0。有可能是因为这个因为导致没有触发young GC,

而且看了一些的文档,写了java版本1.7.60以上对G1的支持比较好,目前我们的版本是1.7.45。

:升级java版本,以更兼容G1,而且项目正式上线,流量上来之后会经常触发GC,保证及时进行回收。日后再进行观察。

 

本文希望以一个实际的例子来看懂G1的日志,已方便日后的故障排查。需要一些知识储备,比如CMS垃圾回收机制,这个网上资料讲的很详细。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM