CMS GC - Hbase

写在前边:

本文参考原文:HBase最佳实践-CMS GC调优

GC阶段流程如下图:

CMS GC - Hbase

Minor GC 流程:

如上图,对象初始化后被放入Young区的Eden区,当Eden区满了之后,会进行一次GC,清理掉没有引用的对象,将依然存活的对象移到S0区,并且回收Eden区空间,称为一次Minor GC,接着新对象进来,又会放入Eden区,满了之后会检查S0和Eden区存活的对象,将所有存活的对象移到S1区,再回收整个S0和Eden区空间,S0和S1两个区总会有一个区是预留给下次存放存活对象用的。

CMS GC流程:(Concurrent Mark-Sweep)

1. initial-mark:这个阶段虚拟机会暂停所有正在执行的任务。这一过程虚拟机会标记所有 ‘根对象’,所谓‘根对象’,一般是指一个运行线程直接引用到的对象。虽然会暂停整个JVM,但因为’根对象’相对较少,这个过程通常很快。
2. concurrent mark:垃圾回收器会从‘根节点’开始,将所有引用到的对象都打上标记。这个阶段应用程序的线程和标记线程并发执行,因此用户并不会感到停顿。
3. concurrent precleaning:并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行mark阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。
4. remark:在阶段3的基础上对查找到的对象进行重新标记,这一阶段会暂停整个JVM,但是因为阶段3已经欲检查出了所有新进入的对象,因此这个过程也会很快。
5. concurrent sweep:上述3阶段完成了引用对象的标记,此阶段会将所有没有标记的对象作为垃圾回收掉。这个阶段应用程序的线程和标记线程并发执行。

6. concurrent reset:重置CMS收集器的数据结构,等待下一次垃圾回收。

Full GC的两种场景:

1、 Concurrent Failure:Tenured区 满了之后,正在进行CMS GC时,从young区有新的对象过来,tenured空间不足,导致STW

这种场景其实比较简单,假如现在系统正在执行CMS回收老生代空间,在回收的过程中新生代来了一批对象进来,不巧的是,老生代已经没有空间再容纳这些对象了。这种场景下,CMS回收器会停止继续工作,系统进入 ’stop-the-world’ 模式,并且回收算法会退化为单线程复制算法,重新分配整个堆内存的存活对象到S0中,释放所有其他空间。很显然,整个过程会非常’漫长’。但是这种问题也很容易解决,只需要让CMS回收器更早一点回收就可以避免。JVM提供了参数-XX:CMSInitiatingOccupancyFraction=N来设置CMS回收的时机,其中N表示当前老生代已使用内存占新生代总内存的比例,该值默认为68,可以将该值修改的更小使得回收更早进行。

2、Promotion Failure:Tenured区大量内存碎片导致迁移过来的新对象无法被接收 

假设此时设置XX:CMSInitiatingOccupancyFraction=60,但是在已使用内存还没有达到总内存60%的时候,已经没有空间容纳从新生代迁移的对象了。oh,my god!怎么会这样?罪魁祸首就是内存碎片,上文中提到CMS算法会产生大量碎片,当碎片容量积累到一定大小之后就会造成上面的场景。这种场景下,CMS回收器一样会停止工作,进入漫长的 ’stop-the-world’ 模式。JVM也提供了参数 -XX: UseCMSCompactAtFullCollection来减少碎片的产生,这个参数表示会在每次CMS回收垃圾之后执行一次碎片整理,很显然,这个参数会对性能有比较大的影响,对HBase这种对延迟敏感的业务来说并不是一个完美解决方案。

基本参数:

-Xmx   最大堆内存
-Xms 初始JVM堆内存
-Xmn young区堆内存
-Xss 分配给每个线程的堆栈内存
-XX:MaxPermSize 分配给持久代的内存大小 (加载外部方法、class)
-XX:SurvivorRatio=3 Eden:Survivor = 3:(1:1) young区 Eden与Survivor的内存比例关系(Survivor有两个区)
-XX:+UseConcMarkSweepGC 表示回收器使用CMS GC策略
-XX:+UseParNewGC 表示young区使用并行回收机制
-XX:+CMSParallelRemarkEnabled 表示CMS的Remark阶段采用并行方式
-XX:MaxTenuringThreshold 表示从young区晋升到tenured阈值
-XX:+UseCMSCompactAtFullCollection 表示每次执行完CMS GC后执行一次碎片合并
-XX:+UseCMSInitiatingOccupancyOnly 表示CMS GC只基于参数CMSInitiatingOccupancyFraction触发
-XX:CMSInitiatingOccupancyFraction 表示Tenured区内存使用量与Tenured总大小的百分比超过该阈值之后会触发CMS GC,该值一般设置为70%-80%

-XX:-DisableExplicitGC 表示禁止使用命令System.gc(),该命令用于触发整个JVM的垃圾回收,一般都是长时间的full gc


调优参数:

-Xmx5g -Xms5g -Xmn512m -Xss256k -XX:MaxPermSize=256m -XX:SurvivorRatio=2  -XX:+UseConcMarkSweepGC -XX:+UseParNewGC 

-XX:+CMSParallelRemarkEnabled -XX:MaxTenuringThreshold=15 -XX:+UseCMSCompactAtFullCollection  -XX:+UseCMSInitiatingOccupancyOnly        

-XX:CMSInitiatingOccupancyFraction=75 -XX:-DisableExplicitGC