转载请注明出处哈:http://carlosfu.iteye.com/blog/2237511
感谢博主:hot66hot.iteye.com/
一:BigMemory如何使用DirectMemory内存
以下是bigMemory启动时打印的DirectMemory分区概述:
Minimum Chunk Size : 8MB
Maximum Chunk Size : 32MB
Concurrency : 16
Initial Segment Table Size : 64 slots
Segment Data Page Size : 64KB
根据日志,可以猜测出BigMemory预先将数据空间划分为一系列Chunk,目的为了防止内存碎片化,与Memcache内存分配策略很像.
借用下memcache 的chunk空间分配过程图,可以更好的理解.
有个问题:bigMemory必须知道存储的对象所占用的空间,才能选择合适的chunk存放对象.
二:BigMemory计算对象所占空间
EHCache计算一个实例占用的内存大小。
基本思路:遍历实例数上的所有节点,对每个节点计算其占用的内存大小。
使用反射的方式计算一个实例占用的内存大小。
反射计算一个实例(instance)占用内存大小(size)过程如下:
a. 如果instance为null,size为0,直接返回。
b. 如果instance是数组类型,size为数组头部大小+每个数组元素占用大小* 数组长度+填充到对象对齐最小单位。
c. 如果instance是普通实例,size初始值为对象头部大小,然后找到对象对应类的所有继承类,从最顶层类开始遍历所有类,对每个类,纪录长整型和双精度型、整型和浮点型、短整型和字符型、布尔型和字节型以及引用类型的非静态字段的个数。在所有类计算完成后,按类对齐规则对齐等
参考资料:http://www.importnew.com/1305.html
EHCache中的SizeOf类中采用deepSize计算,它的步骤是:使用ObjectGraphWalker遍历一个实例的所有对象引用,在遍历中通过使用传入的SizeOfFilter过滤掉那些不需要的字段,然后调用传入的Visitor对每个需要计算的实例做计算。
ObjectGraphWalker的实现算法使用了Stack,也可以使用Queue,这个影响遍历的顺序,深度优先还是广度优先的区别。它抽象了SizeOfFilter接口,可以用于过滤掉一些不想用于计算内存大小的字段,如Element中的key字段。SizeOfFilter提供了对类和字段的过滤:
public interface SizeOfFilter { // Returns the fields to walk and measure for a type CollectionfilterFields(Class> clazz, Collection fields); // Checks whether the type needs to be filtered boolean filterClass(Class> klazz); }
SizeOfFilter的实现类可以用于过滤过滤掉@IgnoreSizeOf注解的字段和类,以及通过net.sf.ehcache.sizeof.filter系统变量定义的文件,读取其中的每一行为包名或字段名作为过滤条件。最后,为了性能考虑,它对一些计算结果做了缓存。
结论: Bigmemory的主要开销:序列化+sizeOf计算
三:sizeOf引擎优化与测试:
1:对sizeOf引擎友好的对象:尽量使用不深/不广的对象:深(继承树) 广( bigPojo,ArrayList,HashMap等)
2:这边使用了大量的ArrayList和HashMap 等对象.日志给出bigMemory警告如下,
为了减少sizeOf计算开销,加入配置:计算超过2000次后终止,但这样会造成chunk分配混乱.
3:采用protostuff预前序列化的方式,bigMemory只存protostuff序列化后的byte数组
如下图:
4:测试用例: 测试采用单线程压测100W次,directMemory空间为100MB.,对象实际值完全相同.
四、结论:
1. 采用预序列化(protostuff)之后set性能有3倍的提高,get性能提高50%以上。
2. 随着对象复杂度增加,相同空间预序列化(protostuff)方式占用空间更少,如HashMap的测试用例.
3. 但是如果对象是简单的pojo,则原生的bigMemory占用空间更有优势.
4. 根据自身系统的对象类型做处理.