老年代 CMS gc回收算法 对hbase的影响*****
参考链接: 深入研究java gc https://blog.51cto.com/12445535/2372976
CMS失败模式(CMS Failure Mode)
1、上文提到在正常的情况下CMS整个流程的暂停时间都是很短的,一般也就在10ms~100ms左右。
2、然而这与线上的情况并不相符,线上集群在读写压力很大的情况下,经常会出现长时间的卡顿,有些卡顿甚至长达几分钟,导致很严重的读写阻塞,甚至会造成Region Server和Zookeeper之间Session超时,使得Region Server异常离线。
3、实际上,CMS并不是很完美,它会在两种场景下产生严重的Full GC(Concurrent Failure(并发失败),Promotion Failure (促销失败)),接下来分别进行介绍。
Concurrent Failure(并发失败)
为什么会出现并发失败?
1、假如现在系统正在执行CMS回收老生代空间,在回收的过程中新生代来了一批对象进来,不巧的是,老生代已经没有空间再容纳这些对象了。
2、这种场景下,CMS回收器会停止继续工作,系统进入 ’stop-the-world’ 模式,并且回收算法会退化为单线程复制算法,重新分配整个堆内存的存活对象到S0中,释放所有其他空间。很显然,整个过程会非常’漫长’。
解决方法:
JVM提供了参数-XX:CMSInitiatingOccupancyFraction=N来设置CMS回收的时机,其中N表示当前老生代已使用内存占新生代总内存的比例,该值默认为68,可以将该值修改的更小使得回收更早进行。
//cdh中 默认值为70
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=70 表示 老年代堆空间的使用率
Promotion Failure (促销失败)
1、假设此时设置XX:CMSInitiatingOccupancyFraction=60,但是在已使用内存还没有达到总内存60%的时候,已经没有空间容纳从新生代迁移的对象了。
2、oh,my god!怎么会这样?罪魁祸首就是内存碎片,上文中提到CMS算法会产生大量碎片,当碎片容量积累到一定大小之后就会造成上面的场景。
3、这种场景下,CMS回收器一样会停止工作,进入漫长的 ’stop-the-world’ 模式。
4、JVM也提供了参数 -XX: UseCMSCompactAtFullCollection来减少碎片的产生,这个参数表示会在每次CMS回收垃圾之后执行一次碎片整理,很显然,这个参数会对性能有比较大的影响,对HBase这种对延迟敏感的业务来说并不是一个完美解决方案。
//-XX: UseCMSCompactAtFullCollection 此参数,在cdh中默认没有
1、在实际线上环境中,很少出现Concurrent Failure模式的Full GC,大多数Full GC场景都是Promotion Failure。
2、我们线上集群也会每隔半个月左右就会因为Promotion Failure触发一次Full GC。
3、为了更好地理解CMS策略下内存碎片是如何触发Promotion Failure,接下来我们做一个简单的实验:
4、JVM提供了参数 -XX:PrintFLSStatistics=1来打印每次GC前后内存碎片的统计信息,统计信息主要包括3个维度:Free Space、Max Chunk Size和Num Chunks,
5、其中Free Space表示老生代当前空闲的总内存容量,
6、Max Chunk Size表示老生代中最大的内存碎片所占的内存容量大小,
7、Num Chunks表示老生代中总的内存碎片数。
8、我们在测试环境集群(共4台Region Server)将这个参数设置为1,然后使用一个客户端YCSB执行Read-And-Write操作,分别统计日志中Free Space和Max Chunk Size两个指标随时间的变化情况。
小结:
可以知道:CMS GC会不断产生内存碎片,当碎片小到一定程度之后就会基本维持不变,如果此时业务写入一些单条数据量很大的KeyValue,就有可能触发Promotion Failure模式Full GC。
参考链接:
http://hbasefly.com/2016/05/21/hbase-gc-1/
原文地址:https://blog.51cto.com/12445535/2373206