原文:http://yueyemaitian.iteye.com/blog/2033046
老早之前写过一篇博客,是关于一个Integer对象到底占用多少字节的,现在看来,那篇文章竟然计算错了。这次再去计算,是因为之前写的一篇关于字长的文章里,看到了hotspot jvm里,对象占用空间是8字节对齐的,再加上之前关于字节那文章里带着一点-XX:+UseCompressedOops压缩指针参数的疑问,重新探究了下一个对象到底占用多少字节,以及如何计算它占用空间的方法。主要是参考了这篇很久以前的文章,不过试验了一把,instrumentation这种方法还是靠谱的。
1 Premain-class: xxx.yyy.zzz.SizeOfObject 2 Can-Redefine-Classes: false 3 Boot-Class-Path:
来看看测试类:
1 import java.io.File; 2 import static com.tmall.buy.structure.SizeOfObject.*; 3 /** 4 * @author tianmai.fh 5 * @date 2014-03-18 20:17 6 */ 7 public class SizeOfObjectTest { 8 /** 9 * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 = 16 10 * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + padding/4 = 24 11 */ 12 static class A { 13 int a; 14 } 15 16 /** 17 * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24 18 * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + 4 = 24 19 */ 20 static class B { 21 int a; 22 int b; 23 } 24 25 /** 26 * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24 27 * -XX:-UseCompressedOops: mark/8 + metedata/8 + 8 + 4 + padding/4 = 32 28 */ 29 static class B2 { 30 int b2a; 31 Integer b2b; 32 } 33 34 /** 35 * 不考虑对象头: 36 * 4 + 4 + 4 * 3 + 3 * sizeOf(B) 37 */ 38 static class C extends A { 39 int ba; 40 B[] as = new B[3]; 41 42 C() { 43 for (int i = 0; i < as.length; i++) { 44 as[i] = new B(); 45 } 46 } 47 } 48 49 static class D extends B { 50 int da; 51 Integer[] di = new Integer[3]; 52 } 53 54 /** 55 * 会算上A的实例字段 56 */ 57 static class E extends A { 58 int ea; 59 int eb; 60 } 61 62 public static void main(String[] args) throws IllegalAccessException { 63 System.out.println(new File("./target/classes").getAbsolutePath()); 64 System.out.println("sizeOf(new Object())=" + sizeOf(new Object())); 65 System.out.println("sizeOf(new A())=" + sizeOf(new A())); 66 System.out.println("sizeOf(new B())=" + sizeOf(new B())); 67 System.out.println("sizeOf(new B2())=" + sizeOf(new B2())); 68 System.out.println("sizeOf(new B[3])=" + sizeOf(new B[3])); 69 System.out.println("sizeOf(new C())=" + sizeOf(new C())); 70 System.out.println("fullSizeOf(new C())=" + fullSizeOf(new C())); 71 System.out.println("sizeOf(new D())=" + sizeOf(new D())); 72 System.out.println("fullSizeOf(new D())=" + fullSizeOf(new D())); 73 System.out.println("sizeOf(new int[3])=" + sizeOf(new int[3])); 74 System.out.println("sizeOf(new Integer(1)=" + sizeOf(new Integer(1))); 75 System.out.println("sizeOf(new Integer[0])=" + sizeOf(new Integer[0])); 76 System.out.println("sizeOf(new Integer[1])=" + sizeOf(new Integer[1])); 77 System.out.println("sizeOf(new Integer[2])=" + sizeOf(new Integer[2])); 78 System.out.println("sizeOf(new Integer[3])=" + sizeOf(new Integer[3])); 79 System.out.println("sizeOf(new Integer[4])=" + sizeOf(new Integer[4])); 80 System.out.println("sizeOf(new A[3])=" + sizeOf(new A[3])); 81 System.out.println("sizeOf(new E())=" + sizeOf(new E())); 82 } 83 }
如果你是用maven打包的话,可以考虑在pom.xml文件中配置。打完jar包后,可以直接运行SizeOfObject了,但是要加上vm启动参数(test.jar是刚才打的jar包):
1 -javaagent:target/test.jar
在我64bit mac上,跑64位hotspot vm的结果如下,其中压缩对象指针参数是开启的,即-XX:+UseCompressedOops
1 sizeOf(new Object())=16 2 sizeOf(new A())=16 3 sizeOf(new B())=24 4 sizeOf(new B2())=24 5 sizeOf(new B[3])=32 6 sizeOf(new C())=24 7 fullSizeOf(new C())=128 8 sizeOf(new D())=32 9 fullSizeOf(new D())=64 10 sizeOf(new int[3])=32 11 sizeOf(new Integer(1)=16 12 sizeOf(new Integer[0])=16 13 sizeOf(new Integer[1])=24 14 sizeOf(new Integer[2])=24 15 sizeOf(new Integer[3])=32 16 sizeOf(new Integer[4])=32 17 sizeOf(new A[3])=32 18 sizeOf(new E())=24
如果关闭指针压缩,即在vm启动参数中加上-XX:-UseCompressedOops结果会不一样:
1 sizeOf(new Object())=16 2 sizeOf(new A())=24 3 sizeOf(new B())=24 4 sizeOf(new B2())=32 5 sizeOf(new B[3])=48 6 sizeOf(new C())=40 7 fullSizeOf(new C())=160 8 sizeOf(new D())=40 9 fullSizeOf(new D())=88 10 sizeOf(new int[3])=40 11 sizeOf(new Integer(1)=24 12 sizeOf(new Integer[0])=24 13 sizeOf(new Integer[1])=32 14 sizeOf(new Integer[2])=40 15 sizeOf(new Integer[3])=48 16 sizeOf(new Integer[4])=56 17 sizeOf(new A[3])=48 18 sizeOf(new E())=32
UseCompressOops开启和关闭,对对象头大小是有影响的,开启压缩,对象头是4+8=12byte;关闭压缩,对象头是8+8=16bytes。这个如何观察验证呢?
基于上述事实,通过new A()和new B()占用字节推断,基本类型int在开启、关闭压缩情况下都是占用4个bytes的,这个没有影响。而通过B和B2在开启、关闭指针压缩情况下的对比看,Integer类型分别占了4 bytes和8 bytes,实际上引用类型都是这样。如何验证?
new Integer[0]在压缩前后分别占用16、24个字节,这是又是为什么呢?
欲知后事,且听下回分解!enjoy it !