千家信息网

HBase最佳实践-CMS GC调优(从gc本身参数调优)

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,同志们,此部分,重要的不能再重要了1、HBase发展到当下,对其进行的各种优化从未停止,而GC优化更是其中的重中之重。hbase gc调优方向从0.94版本提出MemStoreLAB策略、Memsto
千家信息网最后更新 2026年11月02日HBase最佳实践-CMS GC调优(从gc本身参数调优)

同志们,此部分,重要的不能再重要了
1、HBase发展到当下,对其进行的各种优化从未停止,而GC优化更是其中的重中之重。
hbase gc调优方向
从0.94版本提出MemStoreLAB策略、Memstore Chuck Pool策略对写缓存Memstore进行优化开始,到0.96版本提出BucketCache以及堆外内存方案对读缓存BlockCache进行优化,再到后续2.0版本宣称会引入更多堆外内存,可见HBase会将堆外内存的使用作为优化GC的一个战略方向。

然而无论引入多少堆外内存,都无法避免读写全路径使用JVM内存,就拿BucketCache中offheap模式来讲,即使HBase数据块是缓存在堆外内存的,但是在读取的时候还是会首先将堆外内存中的block加载到JVM内存中,再返回给用户。
这句话的理解在:http://hbasefly.com/2016/04/26/hbase-blockcache-2/
//提到了
BucketCache工作模式 //
比如,内存分配时heap模式需要首先从操作系统分配内存再拷贝到JVM heap,相比offheap直接从操作系统分配内存更耗时;但是反过来,读取缓存时heap模式可以从JVM heap中直接读取,而offheap模式则需要首先从操作系统拷贝到JVM heap再读取,显得后者更费时。

可见,无论使用多少堆外内存,对JVM内存的使用终究是绕不过去,既然绕不过去,就还是需要落脚于GC本身,对GC本身进行优化。

回顾CMS GC 算法的工作原理
先上图:

详细请见:https://blog.51cto.com/12445535/2372976

文中对JVM的内存结构以及CMS GC进行了相当详细的介绍。

  1. 整个JVM内存由Young区、Tenured区和Perm区三部分组成,其中Young区又分为一个Eden区和两个Survivor区
  1. 整个对象生命周期简要说明(一定要烂熟于心,下文会一直用到):
    (1)Young区:一个对象初始化之后,首先会进入Eden区,当Eden区满之后会触发一次Minor GC,Minor GC会检查Eden区所有对象是否依旧存活(是否有其他对象引用),如果存活,会将其从Eden区拷贝到Survivor区,并将这些存活对象的age加一,而死亡的对象会被作为垃圾回收。此时Eden区又空闲出来,等新对象填充,填充满之后再会触发Minor GC,如此往复。需要注意的是,每执行一次Minor GC,存活对象的age就会加一。
    (2)Tenured区:一旦存活对象的age超多一定阈值就会晋升到Tenured区,因此可以理解为Tenured区一般存放长寿对象。很显然,随着时间流逝,Tenured区也会被填充满,此时就会触发CMS GC(old gc),这种GC相对比较复杂,由6个步骤组成分别为(初始标记;并发标记;并发预清理;重新标记;并发清理;并发重置。),详见参考文章。
  2. 无论是Minor GC还是CMS GC,都会'Stop-The-World',即停止用户的一切线程,只留下gc线程回收垃圾对象。其中Minor GC的STW时间主要耗费在复制阶段(年轻代本身就是copy算法),CMS GC的STW时间主要耗费在标示垃圾对象阶段(也就是并发标记和并发预清理阶段)。

先来看看GC调优的最终目标和基本原则:

  1. 平均Minor GC时间尽可能短。因为整个Minor GC都处于STW(STW时间主要耗费在复制阶段),因此短时间Minor GC会使用户读写更加平稳,延迟可控。
  2. CMS GC次数越少越好。时间越短越好。一方面是因为一次CMS GC一般都会引起至少秒级的应用暂停,对用户读写影响较大;另一方面频繁的CMS GC会产生大量的内存碎片,严重的时候会引起Full GC,导致RegionServer宕机。

下面对参数的调优技巧都谨遵以上原则,尤其对于HBase这类延迟敏感性项目而言,在尽量避免严重影响用户读写的情况下使得GC更加平稳、暂停时间更短!
//也就是遵循一个原则 不管是minor gc还是cms gc尽量时间段,减少cms gc的次数。

CMS GC优化技巧(三个阶段 )

主要分三个阶段进行。
1、第一阶段会介绍适用于所有场景下的GC参数配置,这些参数不需要太多解释读者就可以轻松理解;
2、第二阶段和第三阶段分别就两组参数进行调优讲解,这两组参数一般会根据不同的应用场景进行设置才能使得GC效果最好,鉴于这两组参数的复杂性,

阶段一:默认推荐配置 //每个参数什么意思讲解
-Xmx -Xms -Xmn -Xss -XX:MaxPermSize= M -XX:SurvivorRatio=S -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:MaxTenuringThreshold=N -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=C -XX:-DisableExplicitGC

-Xmx:分配给JVM的最大堆内存
-Xms:分配给JVM的初始内存,此值一般和Xmx设置相同
-Xmn:分配给young区内存大小,此值的设置对系统性能影响很大,后面第二阶段将会重点讨论此值的参数的调优
-Xss:分配给每个线程的堆栈大小,在一些对线程数敏感的系统中该值设置比较重要,一般设置为256K~1M左右
-XX:MaxPermSize= M :分配给持久代的内存大小
-XX:SurvivorRatio=S : 表示young区中eden区和survivor区的内存大小比例,默认为8 该值的设置对系统性能影响很大,第三阶段会重点讨论该参数的调优

-XX:+UseConcMarkSweepGC :表示回收器使用CMS CG策略
-XX:+UseParNewGC :表示young区采用并行回收机制 推荐使用 &&&&&
-XX:+CMSParallelRemarkEnabled : 表示cms的remark阶段采用并行的方式,推荐使用 &&&&&
-XX:MaxTenuringThreshold=N : 表示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 推荐使用 &&&&&
-XX:+PrintTenuringDistribution才能打印对应日志,强烈建议线上集群开启该参数,&&&&&

使用所有场景下的gc参数调优
*****通过上文对各个GC参数的说明,可以轻松得出第一阶段推荐的参数设置如下,这样的设置基本适用于所有的场景:
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=75% -XX:-DisableExplicitGC

调优预准备

1、上文通过解释各个GC参数意义给出了基本的推荐设置,同时也提到几个对性能影响重大的参数:
2、Xmn、SurvivorRatio以及MaxTenuringThreshold,下面会通过理论推理+实验验证的方式对这几个参数在HBase系统的设置进行调优。

需要强调的是HBase全部配置为BucketCache模式,而不是LruBlockCache。使用了大量堆外内存作为读缓存,在很大程度上优化了GC
可见BucketCache模式比LruBlockCache模式GC表现好很多,强烈建议线上配置BucketCache模式。

GC日志分析

介绍完实验基本条件后,再对GC日志进行简单的解释,方便下文对日志进行分析。需要注意只有在添加参数-XX:+PrintTenuringDistribution才能打印对应日志,强烈建议线上集群开启该参数,

HBase场景内存分析
因此可以看出,HBase系统属于长寿对象居多的工程,因此GC的时候只需要将RPC这类短寿对象在Young区淘汰掉就可以达到最好的GC效果。

阶段二:NewParSize调优 // 也就是 -Xmn 参数 //细节看博客内存

NewParSize表示young区大小,而young区大小直接决定minor gc的频率。
minor gc频率一方面决定单次minor gc的时间长短,gc越频繁,gc时间就越短;一方面决定对象晋升到老年代的量,gc越频繁,晋升到老年代的对象量就越大。解释起来就是:

  1. 增大young区大小,minor gc频率降低,单次gc时间会较长(young区设置更大,一次gc就需要复制更多对象,耗时必然比较长),业务读写操作延迟抖动较大。反之,业务读写操作延迟抖动较小,比较平稳。
  2. 减小young区大小,minor gc频率增快,但会加快晋升到老年代的对象总量(每gc一次,对象age就会加一,当age超过阈值就会晋升到老年代,因此gc频率越高,age就增加越快),潜在增加old gc风险。

因此设置NewParSize需要进行一定的平衡,不能设置太大,也不能设置太小。

小结:
1、Xmn=2是一个最优的选择
//Xmn设置过小会导致CMS GC性能较差,而设置过大会导致Minor GC性能较差,
//因此建议在JVM Heap为64g以上的情况下设置Xmn在1~3g之间
//在32g之下设置为512m~1g
细节:
具体最好经过简单的线上调试;需要特别强调的是,笔者在很多场合都看到很多HBase线上集群会把Xmn设置的很大,比如有些集群Xmx为48g,Xmn为10g,查看日志发现GC性能极差:单次Minor GC基本都在300ms~500ms之间,CMS GC更是很多超过1s。在此强烈建议,将Xmn调大对GC(无论Minor GC还是CMS GC)没有任何好处,不要设置太大。

阶段三:增大Survivor区大小(减小SurvivorRatio) & 增大MaxTenuringThreshold
1、-XX:SurvivorRatio=S : 表示young区中eden区和survivor区的内存大小比例,默认为8 该值的设置对系统性能影响很大,第三阶段会重点讨论该参数的调优
2、-XX:MaxTenuringThreshold=N : 表示young区对象晋升到Tenured区的阈值,该值的设置对系统的影响很大,在第三节点会重点讨论

小结:
1、一般情况下,默认MaxTenuringThreshold=15已经相对比较大,不需要做任何调整。
2、对于Minor GC来说,SurvivorRatio设置对其影响不是很大。而对于CMS GC来说,将SurvivorRatio设置过大简直就是灾难,性能极其差。而和默认值SurvivorRatio=8相比,将SurvivorRatio调小有利于短寿小对象更充分地淘汰,因此建议将SurvivorRatio=2

CMS调优结论

  1. 缓存模式采用BucketCache策略Offheap模式
  2. 对于大内存(大于64G),采用如下配置:
    -Xmx64g -Xms64g -Xmn2g -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

总结:
1、其中Xmn可以随着Java分配堆内存增大而适度增大,但是不能大于4g,取值范围在1~3g范围;
2、SurvivorRatio一般建议选择为2;
3、MaxTenuringThreshold设置为15;
4、对于小内存(小于64G),只需要将上述配置中Xmn改为512m-1g即可

最后小结:
通用场景
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=75% -XX:-DisableExplicitGC -XX:+PrintTenuringDistribution

cms gc调优(小于64G内存的)
-Xmx""g -Xms""g -Xmn1g -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 -XX:+PrintTenuringDistribution

cms gc对于大内存(大于64G),采用如下配置:
-Xmx64g -Xms64g -Xmn2g -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 -XX:+PrintTenuringDistribution

小伙伴的问题1: 关于gc日志的问题
博主您好,想请教一下,gc的日志,/var/log/hbase/gc.regionserver.log每次重启就会覆盖,能够配置成追加写吗。有时hbase报错,想去查gc的问题时,一旦重启就没了之前的gc日志信息了。
答:
可以看看你的jvm配置 将GC日志设为3或者更多 -Xloggc:$HBASE_LOG_DIR/gc-regionserver-date +%Y%m%d-%H-%M.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3

小伙伴的问题2:
博主你好,我也是做了好多测试,发现不同读写比情况下LRU的性能(吞吐、延时)都要强于CBC,看到文章中也提到了这一点,但还是想不懂为什么CBC要差一些,所以很想请教博主?
答:
因为CBC模式下bucketcache(offheap模式)使用堆外内存,堆外内存读取会比jvm内存读取复杂,流程更多,所以在全内存场景喜爱LRU完全好于CBC,在缓存基本不命中场景下两者吞吐量延迟基本相当

全内存场景是指?

数据量比较小或者有大量热点读的场景 大多数读都落在BlockCache场景

小伙伴的问题3:
范大神,能问下你们hbase集群zookeeper.session.timeout这个参数设置多大么,能否通过调大这个参数来降低因为GC导致的RS连接zk超时挂掉问题呢? 假如我调整到比如180秒会对hbase造成什么影响吗? 万分感谢!!
答:
离线集群的话设置大点没啥问题 实时在线对延迟敏感的集群就不能设置太大

小伙伴的问题:
博主方便用 jmap -heap PID 显示出博主的配置,然后解释这些参数吗?这样可能更加方便大家看学习博主的配置。
我这边的答案为
[root@hdfs-master-80-121 hbase]# ps -ef|grep hbase
hbase 3004 2490 0 Mar06 ? 00:54:30 /usr/java/jdk1.8.0_102/bin/java -Dproc_regionserver -XX:OnOutOfMemoryError=kill -9 %p -Djava.net.preferIPv4Stack=true -Xms4294967296 -Xmx4294967296 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/hbase_hbase-REGIONSERVER-e72e026ce56e3850d7702a2ca6ecc206_pid3004.hprof -XX:OnOutOfMemoryError=/usr/lib64/cmf/service/common/killparent.sh -Dhbase.log.dir=/var/log/hbase -Dhbase.log.file=hbase-cmf-hbase-REGIONSERVER-hdfs-master-80-121.log.out -Dhbase.home.dir=/opt/cloudera/parcels/CDH-5.9.2-1.cdh6.9.2.p0.3/lib/hbase -Dhbase.id.str= -Dhbase.root.logger=INFO,RFA -Djava.library.path=/opt/cloudera/parcels/CDH-5.9.2-1.cdh6.9.2.p0.3/lib/hadoop/lib/native:/opt/cloudera/parcels/CDH-5.9.2-1.cdh6.9.2.p0.3/lib/hbase/lib/native/Linux-amd64-64 -Dhbase.security.logger=INFO,RFAS org.apache.hadoop.hbase.regionserver.HRegionServer start
hbase 3090 3004 0 Mar06 ? 00:26:57 /bin/bash /usr/lib64/cmf/service/hbase/hbase.sh regionserver start
hbase 6981 3090 0 18:04 ? 00:00:00 sleep 1
root 6983 21889 0 18:04 pts/0 00:00:00 grep --color=auto hbase

[root@hdfs-master-80-121 hbase]# jmap -heap 3004
Attaching to process ID 3004, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.102-b14

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 4294967296 (4096.0MB)
NewSize = 348913664 (332.75MB)
MaxNewSize = 348913664 (332.75MB)
OldSize = 3946053632 (3763.25MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 314048512 (299.5MB)
used = 94743000 (90.35396575927734MB)
free = 219305512 (209.14603424072266MB)
30.168269034817143% used
Eden Space:
capacity = 279183360 (266.25MB)
used = 91102232 (86.8818588256836MB)
free = 188081128 (179.3681411743164MB)
32.63168406598445% used
From Space:
capacity = 34865152 (33.25MB)
used = 3640768 (3.47210693359375MB)
free = 31224384 (29.77789306640625MB)
10.442426867951127% used
To Space:
capacity = 34865152 (33.25MB)
used = 0 (0.0MB)
free = 34865152 (33.25MB)
0.0% used
concurrent mark-sweep generation:
capacity = 3946053632 (3763.25MB)
used = 16010552 (15.268852233886719MB)
free = 3930043080 (3747.9811477661133MB)
0.4057357931013544% used

14606 interned Strings occupying 1393920 bytes.

小伙伴的问题6:
您好,请问您为什么没有使用G1 GC机制呢?
答:
没有用G1GC一方面是因为我们这边内存使用量没有那么大,另一方面G1GC要用好需要关注太多参数配置。不过以后是一个大的方向

请问有G1调优的吗 我等了三年了

可以参考下:http://openinx.github.io/ppt/hbaseconasia2017_paper_18.pdf

参考链接:
http://hbasefly.com/2016/08/09/hbase-cms-gc/

内存 参数 对象 阶段 模式 大小 时间 配置 场景 系统 日志 影响 性能 问题 分配 很大 建议 缓存 集群 推荐 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技工作委员会 中关村互联网 科技集团 涨乐互联网科技 广州凡科互联网科技 碳银互联网科技有限公司 广州紫鲸互联网科技 互联网科技视频素材 滨湖区互联网智能科技产品供应 互联网巨头投资的硬核科技 富国互联网科技创新基金 云创客互联网科技公司 中国互联网有什么科技含量 崇明区互联网教育科技诚信经营 深圳市优智互联网科技有限公司 科技赋能视角下互联网保险发展 宇信科技在杭州互联网排第几 信承诺互联网科技有限公司 玉溪新华互联网科技怎么选 如何强化互联网金融科技 株洲百竞科技的互联网营销顾问 科技互联网技术 西安互联网科技 互联网科技媒体 郑州华宇互联网科技 海南购全球互联网科技 卫星互联网科技股票 银川新华互联网科技学校联系电话 深圳艾果互联网通讯科技 国大互联网教育科技有限公司薪资 10位科技互联网大佬
0