JAVA对象内存layout

       迟到的文章,4年前就有所研究,但是总是用的时候现看,补上这篇blog,方便自己查询记忆。 通常情况上来说,Java程序员不需要了解对象在内存中的情况,这是一件非常幸福的事情,不需要malloc,free等等,更不需要管垃圾回收。但是,在一些情况下,比如内存占用非常大的应用(像hadoop namenode),就需要对内存占用十分敏感,比如我之前的blog曾经计算过,如果大规模设置ACLs后对hadoop集群内存的压力。 这种情况就需要仔细的计算java类的内存使用情况,一点点的内存节省,当面临成千万的对象的时候效果就会很明显。

       JAVA对象内存计算分为shallow size和deep size。shallow size就是这个对象自己占用的内存空间,包括有基本数据类型占用的内存,其它reference对象的reference 占用的内存。和deep size就是这个除了这个对象自己占用的内存空间,再加上reference对象的占用内存综合。我更感兴趣的是deep size。 一下所有计算都是按照64 bit进行的,32 bit请自行查询。默认情况下java6都是开启-XX:+UseCompressedOops,会对object 的header计算有影响。

        64 bit object的header压缩的占用12byte,不压缩的占用16byte,包括有对象类型的指针,锁信息,以及hash等等。 基本的类型内存占用如下: mem1

       对象的reference64位不压缩的占用8 bytes, 开启压缩占用4 bytes。

       内存layout规则如下:

       1、对象在内存中都是8字节对齐的,即内存计算后不能被8整除,要补充到8字节对齐

       2、基本数据类型也需要对齐,根据类型占用字节数对齐,比如long、double8字节对齐, int,float 4字节对齐

       3、在多数情况下,由于对齐的原因,JVM会按照类型占用的字节多少排列顺序,占用多的排在前面,占用少的排在后面,对象的reference排在最后。比如:

class MyClass {
   byte a;
   int c;
   boolean d; 
   long e;
   Object f; 
}

       如果按照定义的顺序,那么内存使用如下,总共有14 bytes的padding。

[HEADER: 16 bytes] 16 
[a: 1 byte ] 17
[padding: 3 bytes] 20 
[c: 4 bytes] 24 
[d: 1 byte ] 25 
[padding: 7 bytes] 32 
[e: 8 bytes] 40 
[f: 4 bytes] 44 
[padding: 4 bytes] 48

       如果按照大小排列,也就是2中的规则排列,少使用了6 bytes。

[HEADER: 16 bytes] 16 
[e: 8 bytes] 24 
[c: 4 bytes] 28 
[a: 1 byte ] 29 
[d: 1 byte ] 30 
[padding: 2 bytes] 32 
[f: 4 bytes] 36 
[padding: 4 bytes] 40

       4、class field之间不会混在一起,如果class B extends class A,那么父类所有field先去排列,接下来是子类,同时子类起始位置需要对齐4 byte。

       5、对于数组来说,header除了上面说的以外,还需要加入8 bytes,标识array 长度

Print Friendly

zm

Leave a Reply