`

从JVM堆中对象结构看编程内存优化

 
阅读更多

前一段时间在看关于如何测定JVM中对象占用的内存,涉及到JVM堆中对象结构相关知识。

在网上看到一篇比较不错的文章,分享出来,希望对看到的人有所帮助。大笑

---------------------------------

原文地址http://www.javamex.com/tutorials/memory/object_memory_usage.shtml 

在JVM堆中,每个对象由四个域构成:

1、对象头,占用很少的字节,表述Object当前状态的信息:

“对象头”的作用是用来记录一个对象的实例名字、ID和实例状态(例如,当前实例是否“可到达”,或者当前锁的状态等等)。

注:java在64bit模式下开启指针压缩,比32bit模式下头部会大4byte(_mark区域变成8byte,_class区域被压缩),即12字节。

如果没有开启指针压缩,头部会大8byte(_mark和_class都会变成8byte),即16字节。

jdk1.6参数-XX:+UseCompressedOops(指针压缩),在32G内存下默认会自动开启这个参数。

2、基本类型域占用的空间(原生域指int、boolean、short等):

boolean、byte占用1 byte,char、short占用2 bytes,int、float占用4bytes,long、double占用 8 bytes。

3、引用类型域占用的空间(引用类型域指 其他对象的引用,每个引用占用4个字节):

每个引用类型占用4bytes。

注:对于组合对象(比如String对象)而言,它不会把“被组合”的对象(比如:String中的char[]数组对象)作为自身空间,而是开辟一个引用类型空间来引用组合对象。组合关系是一种对象之间的相互引用,引用和被引用的对象之间相互分离的。在物理空间上,避免了一个对象和其成员对象在空间上必须连续的问题,而是一种逻辑上的Link。同样,被引用对象同样以8字节的倍数进行对齐。

4、填充物占用的空间:

在Hotspot中,每个对象占用的总空间是以8的倍数计算的,对象占用总空间(对象头+声明变量)不足8的倍数时候,自动补齐。而这些被填充的空间,我们可以称它为“填充物”。

注:对象采用8字节对齐的方式是不论32bit还是64bit都是一样的。

另外,如果对象是数组:则至少占用12字节(多出4个字节保存数组长度),同时数组也会以8字节自动补齐。

 

意义:

现在了解了JVM堆中对象的结构,就能在日常编程中对创建的对象估算其占用内存大小,以便进行优化

例如:

1、现在有500个字节存入数组,可以写作

byte[] bytes = new byte[500]; //每个元素存储为一个byte.

或者

Byte[] bytes = new Byte[500]; //每个元素存储为一个Byte.

(1)首先计算一下new byte[500];占用内存大小:

8+4+1*500 = 512 byte

前12个字节是数组中8byte的空Object和4byte的数组长度,数组中每个元素占用1byte。

(2)然后计算new Byte[500];占用内存大小:

8+4+4*500+16*500=10012 byte -> 对齐 = 10016 byte

前12个字节是数组中8byte的空Object和4byte的数组长度,数组中包含500个Byte的引用(4*500),同时由于

每个元素都是Byte对象,共500个(16*500,一个Byte对象要以8字节对齐,所以为16byte)。

以上结果都经过SizeOf工具验证,同时可以参考我的blog:

使用SizeOf测定JVM中对象占用内存 http://shensy.iteye.com/blog/1765760 

结论:使用byte数组存储占用内存0.5k,使用Byte数组存储占用内存近10k

2、编程时,能用Boolean.TRUE或FALSE就不要用new Boolean(),能用Integer.valueOf的时候就不要用new Integer(),其它各数据类型也都如此。原因是这些方法会存储一部分数值放在一块共享内存区域作为缓存使用。详解请见我的blog:

JVM对象占用内存计算工具--SizeOf源码分析 http://shensy.iteye.com/blog/1858987 

结束语:

理解了JVM堆中对象结构,对于日常Java编程中内存优化会有很大帮助。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics