[讨论] Cloudera的工程师关于CMS的内存碎片问题的一个解决方案

ningandjin 2012-01-17
http://www.cloudera.com/blog/2011/02/avoiding-full-gcs-in-hbase-with-memstore-local-allocation-buffers-part-1/

全文分3个部分,第一部分简单介绍了一下JVM的GC概念;关于CMS的基础理论和常见的2个异常concurrent mode failure和promotion failed还比较清晰,第二部分:通过在Hbase上的一系列实验,分析了为什么Hbase会频繁的Full GC,不只是在内存满,或者达到CMS触发比例的时候才会触发Full GC,碎片同样会导致这个问题;第三部分作者讲解了Hbase中为什么会导致内存碎片,以及Hbase是如何通过MemStore-Local Allocation Buffer方案来解决这个内存碎片的问题的。


CMS的内存碎片问题对GC来说应该是一个很常见且比较的问题,尤其是在这个内存白菜价的时代,很多Java应用都是按10G为单位来使用内存,要是因为内存碎片导致频繁FullGC挂起应用程序就太划不来了。

所以,小弟在这儿分享这篇文章,同时希望能听听各位大牛关于CMS的碎片问题都有些什么样的解决方案? 谢谢!
RednaxelaFX 2012-01-17
嗯,这篇之前就读过了。

Arena系的技巧只有在特定场景下才适用。最重要的是,Java是类型安全的,因而使得很多在C、C++里很好用的一些实现技巧在Java里无法使用。
例如说,你不能自己申请一个很大的byte[]之后,把其中的一小块当作一个你自己的Foo类型的普通Java对象实例使用。

假如我们在这个上下文里把Java程序里处理的数据分为有结构和无结构两种;有结构的定义为就是普通Java对象,无结构的定义为诸如byte[]或非Java对象。

那么如果要处理的数据是无结构的,像这种MSLAB就适用;事实上MSLAB的底下的存储根本不需要用byte[],可以换成用DirectByteBuffer,可以进一步降低GC的压力。

而有结构的数据如果只是要不断的序列化出去,那么这种MSLAB也适用,因为“序列化”本身就是一种把有结构数据转换为无结构数据的过程。

CMS的碎片化问题是它的固有问题,是无法避免,只能推迟其引起带compaction的full GC的时机。
要说有啥缓解这问题的办法,那就是Garbage-First GC正在做的事情:边并发收集边压缩。但G1GC也有积累碎片的可能性,最终仍然有一个fallback是完全stop-the-world来做compaction的。

如果用Azul的C4就根治了…如果肯给钱的话,呵呵。
ningandjin 2012-01-18
大家,早上好。
"事实上MSLAB的底下的存储根本不需要用byte[],可以换成用DirectByteBuffer,可以进一步降低GC的压力。 "
MSLAB中的这部分无结构数据是要到年老代的,如果使用DirectByteBuffer,是不是意味着在触发fullGC之前,可能会有相当大一部分本地内存会得不到释放呢?另外,请教一下,一个java对象是怎样占用内存的?比方说,一个java对象。有一个String属性a,长度为5,还有一个String属性b,长度为10. 那么在内存中,这个java对象是占了(8+5+10)个连续的字节呢?跟序列化有关?还是....?  R大推荐些资料吧, 很想了解下,关于JVM内存分配的知识。谢谢。
RednaxelaFX 2012-01-18
MSLAB的“生命结束”如果是在应用程序所可预知的时间发生的话,使用DirectByteBuffer时可以自行显式调用其Cleaner,这样关联的native memory就可以及时释放。这就跟用完InputStream之后应该手动close()(或者在Java 7里用try-with-resources)一样。
依赖GC去收尾其实挺糟糕的。

如果应用程序无法判定MSLAB的“生命结束”是什么时候,那么定期执行System.gc(),并设置 -XX:+ExplicitGCInvokesConcurrent 使System.gc()触发的是CMS GC而不是真的full GC也是一种办法。

Java对象布局请看我之前做的PPT,里面有讲。http://www.tektalk.org/2011/07/28/java-%E7%A8%8B%E5%BA%8F%E7%9A%84%E7%BC%96%E8%AF%91%EF%BC%8C%E5%8A%A0%E8%BD%BD-%E5%92%8C-%E6%89%A7%E8%A1%8C/
跟序列化没关系。
hellhell 2012-01-19
RednaxelaFX 写道
嗯,这篇之前就读过了。

Arena系的技巧只有在特定场景下才适用。最重要的是,Java是类型安全的,因而使得很多在C、C++里很好用的一些实现技巧在Java里无法使用。
例如说,你不能自己申请一个很大的byte[]之后,把其中的一小块当作一个你自己的Foo类型的普通Java对象实例使用。

假如我们在这个上下文里把Java程序里处理的数据分为有结构和无结构两种;有结构的定义为就是普通Java对象,无结构的定义为诸如byte[]或非Java对象。

那么如果要处理的数据是无结构的,像这种MSLAB就适用;事实上MSLAB的底下的存储根本不需要用byte[],可以换成用DirectByteBuffer,可以进一步降低GC的压力。

而有结构的数据如果只是要不断的序列化出去,那么这种MSLAB也适用,因为“序列化”本身就是一种把有结构数据转换为无结构数据的过程。

CMS的碎片化问题是它的固有问题,是无法避免,只能推迟其引起带compaction的full GC的时机。
要说有啥缓解这问题的办法,那就是Garbage-First GC正在做的事情:边并发收集边压缩。但G1GC也有积累碎片的可能性,最终仍然有一个fallback是完全stop-the-world来做compaction的。

如果用Azul的C4就根治了…如果肯给钱的话,呵呵。


有人试用过C4么?我一直只是看到Azul到处有广告,没有看到有什么客户说自己的经验
Global site tag (gtag.js) - Google Analytics