利用Arena Allocation避免HBase触发Full GC

本文深入探讨了HBase中由Region flush引发的内存碎片问题,介绍了MSLAB这一基于ArenaAllocation的解决方案,阐述了其原理、实现方式及如何通过配置参数开启MSLAB,以有效减少内存碎片,提升系统性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Arena Allocation,是一种GC优化技术,它可以有效地减少因内存碎片导致的Full GC,从而提高系统的整体性能。本文介绍Arena Allocation的原理及其在Hbase中的应用-MSLAB。

背景

假设有1G内存,我顺序创建了1百万个对象,每个对象大小1K,Heap会被渐渐充满且每个对象以创建顺序相邻。此时,如果我释放50万个奇数对象,即 1 3 5 7后,剩余空间会多出500M,而这段内存空间就不再连续了。问题出现?
如果我打算new一个2K大小的对象,JVM将无从分配它,因为找不到连续可用的内存空间来容纳这个对象,就算Heap当时还有500M的剩余空间,也无能为力。最终,JVM会选择触发Full GC重新压缩内存使之连续,然后再分配。

结论:触发Full GC,并不只有在内存满或达到触发比例的时候,还有可能是因为内存碎片。

产生内存碎片的主要原因是:

  • 分配的大小不一。
  • 分配的空间不连续。

如何检测因内存碎片触发了Full GC?
通过启动java时,添加 -XX:PrintFLSStatistics=1 参数来打印每次gc前后的Heap余量。较大的余量,可以怀疑Heap中存在内存碎片过多。
另外这篇blog有更详细的图文解释:

http://www.cloudera.com/blog/2011/02/avoiding-full-gcs-in-hbase-with-memstore-local-allocation-buffers-part-2/

HBase中的内存碎片

HBase为了提高写入性能,为每个region添加了一个内存写缓存-Memstore。当单个Memstore的大小达到memstore.size或Heap内存达到hbase.regionserver.global.memstore.upperLimit/lowerLimit百分比限制时,就会触发整个region的flush,最终将所有数据写入HDFS并释放region下所有Memstores占用的内存(GC不一定及时)。

Region flush导致内存碎片的示意图:

左边五颜六色的是不同的region在内存中的位置,它是无序的,因为客户端的请求是无规律的。此时假设黄色的region触发了flush,那么右边将会出现与之对应的多个空洞,即内存碎片。??这张图以region为粒度,仅仅是为了更直观地表示这种现象。真实场景中,这些空洞是更细粒度的KeyValue级对象,它能直接导致创建对象时触发Full GC。

Arena Allocation

Arena Allocation是一种非传统的内存管理方法。它通过顺序化分配内存,内存数据分块等特性使内存碎片粗化,有效改善了内存碎片导致的Full GC问题。

它的原理:

  • 创建一个大小固定的bytes数组和一个偏移量,默认值为0。
  • 分配对象时,将新对象的data bytes复制到数组中,数组的起始位置是偏移量,复制完成后为偏移量自增data.length的长度,这样做是防止下次复制数据时不会覆盖掉老数据(append)。
  • 当一个数组被充满时,创建一个新的数组。
  • 清理时,只需要释放掉这些数组,即可得到固定的大块连续内存。

在Arena Allocation方案中,数组的大小影响空间连续性,越大内存连续性越好,但内存平均利用率会降低。

HBase的解决方案-MSLAB

MSLAB,全称是 MemStore-Local Allocation Buffer,是Cloudera在HBase 0.90.1时提交的一个patch里包含的特性。它基于Arena Allocation解决了HBase因Region flush导致的内存碎片问题。

MSLAB的实现原理(对照Arena Allocation,HBase实现细节):

  • MemstoreLAB为Memstore提供Allocator。
  • 创建一个2M(默认)的Chunk数组和一个chunk偏移量,默认值为0。
  • 当Memstore有新的KeyValue被插入时,通过KeyValue.getBuffer()取得data bytes数组。将data复制到Chunk数组起始位置为chunk偏移量处,并增加偏移量=偏移量+data.length。
  • 当一个chunk满了以后,再创建一个chunk。
  • 所有操作lock free,基于CMS原语。

优势:

  • KeyValue原始数据在minor gc时被销毁。
  • 数据存放在2m大小的chunk中,chunk归属于memstore。
  • flush时,只需要释放多个2m的chunks,chunk未满也强制释放,从而为Heap腾出了多个2M大小的内存区间,减少碎片密集程度。

开启MSLAB

hbase.hregion.memstore.mslab.enabled=true // 开启MSALB
hbase.hregion.memstore.mslab.chunksize=2m // chunk的大小,越大内存连续性越好,但内存平均利用率会降低
hbase.hregion.memstore.mslab.max.allocation=256K // 通过MSLAB分配的对象不能超过256K,否则直接在Heap上分配,256K够大了

详见:http://www.cloudera.com/blog/2011/03/avoiding-full-gcs-in-hbase-with-memstore-local-allocation-buffers-part-3/

<think>嗯,用户想了解Mixed GCFull GC的区别及原因。首先,我需要回顾一下Java中的垃圾回收机制,特别是G1收集器相关的知识。 Mixed GC是G1收集器特有的,主要处理年轻代和部分老年代。而Full GC则发生在整个堆内存,包括年轻代和老年代,通常会导致较长的停顿时间。需要明确两者的触发条件,比如Mixed GC可能在并发标记周期后启动,当老年代占用达到一定阈值时。而Full GC可能是因为内存不足,或者并发失败导致的。 接下来要考虑它们的执行过程。Mixed GC会进行年轻代的回收,同时选择收益高的老年代区域进行回收。而Full GC可能使用单线程的Serial Old收集器,导致较长的STW时间。需要解释为什么Full GC会影响性能,以及如何避免它,比如调整堆大小或优化GC参数。 用户可能还关心如何识别这两种GC的发生,可以通过日志分析,比如查看GC日志中的“Pause Full”或“Pause Mixed”。同时,引用资料中提到使用-verbosegc选项和VisualVM工具,这部分可以结合引用内容来说明监控方法。 最后,确保回答结构清晰,先定义两者,再比较区别,然后解释原因,并提供优化建议。还要注意使用LaTeX格式的数学表达式,比如内存阈值设置,但这个问题可能不需要太多数学公式,可能涉及参数如-XX:InitiatingHeapOccupancyPercent=45这样的数值,用代码块格式呈现。 需要检查是否有误解,比如Mixed GC是否属于并发阶段,或者Full GC的不同触发场景。确保引用的资料正确,比如引用[1]和[2]提到的GC日志和监控工具如何帮助识别这两种GC事件。</think>### Mixed GCFull GC的区别及原因 #### 1. 定义与作用范围 - **Mixed GC(混合垃圾回收)** 是G1垃圾收集器特有的机制,同时回收**年轻代(Young Generation)**和**部分老年代(Old Generation)**。其目标是选择收益最高的老年代区域(Region)进行回收,以控制停顿时间[^1]。 - **Full GC(完全垃圾回收)** 会处理**整个堆内存(包括年轻代、老年代、元空间等)**,通常由内存不足或并发标记失败触发,且会导致较长的STW(Stop-The-World)停顿。 #### 2. 触发条件对比 | 类型 | 触发条件 | |------------|------------------------------------------------------------------------------------------| | Mixed GC | - G1并发标记周期完成后<br>- 老年代占用超过阈值(默认`-XX:InitiatingHeapOccupancyPercent=45`)| | Full GC | - 堆内存无法满足分配需求<br>- 并发标记未完成时发生内存不足<br>- 显式调用`System.gc()`(依赖配置)| #### 3. 执行过程差异 - **Mixed GC过程** 1. 并行回收所有年轻代Region 2. 根据标记结果选择部分老年代Region回收 3. 采用多线程并行处理,控制单次停顿时间(通常10-50ms) - **Full GC过程** 1. 单线程执行(如使用Serial Old收集器) 2. 全堆压缩与回收 3. 停顿时间可能长达数秒[^2] #### 4. 性能影响与优化 - **Mixed GC优化** - 调整`-XX:InitiatingHeapOccupancyPercent`降低老年代回收阈值 - 增加`-XX:ConcGCThreads`提升并发标记速度 - **避免Full GC** - 增大堆内存(`-Xmx`) - 启用`-XX:+ExplicitGCInvokesConcurrent`避免`System.gc()`触发Full GC - 监控并发标记阶段内存使用,防止并发失败 #### 5. 日志识别方法 通过GC日志可区分两者: ```log # Mixed GC示例 [GC pause (G1 Evacuation Pause) (mixed), 0.0151239 secs] # Full GC示例 [Full GC (Allocation Failure) 8192K->6321K(10G), 3.214567 secs] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值