老早之前写过一篇博客,是关于一个Integer对象到底占用多少字节的,现在看来,那篇文章竟然计算错了。这次再去计算,是因为之前写的一篇的文章里,看到了hotspot jvm里,对象占用空间是的,再加上之前关于字节那文章里带着一点-XX:+UseCompressedOops压缩指针参数的疑问,重新探究了下一个对象到底占用多少字节,以及如何计算它占用空间的方法。主要是参考了,不过试验了一把,instrumentation这种方法还是靠谱的。
跑代码前,需要按照那篇很老的文章先打包,这样才能注入Instrumentation实例,打包时候需要在MANIFEST.MF中写入三项值(注意包路径名改成自己的包名):
- Premain-class: xxx.yyy.zzz.SizeOfObject
- Can-Redefine-Classes: false
- Boot-Class-Path:
来看看测试类:
- import java.io.File;
- import static com.tmall.buy.structure.SizeOfObject.*;
- /**
- * @author tianmai.fh
- * @date 2014-03-18 20:17
- */
- public class SizeOfObjectTest {
- /**
- * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 = 16
- * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + padding/4 = 24
- */
- static class A {
- int a;
- }
- /**
- * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
- * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + 4 = 24
- */
- static class B {
- int a;
- int b;
- }
- /**
- * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
- * -XX:-UseCompressedOops: mark/8 + metedata/8 + 8 + 4 + padding/4 = 32
- */
- static class B2 {
- int b2a;
- Integer b2b;
- }
- /**
- * 不考虑对象头:
- * 4 + 4 + 4 * 3 + 3 * sizeOf(B)
- */
- static class C extends A {
- int ba;
- B[] as = new B[3];
- C() {
- for (int i = 0; i < as.length; i++) {
- as[i] = new B();
- }
- }
- }
- static class D extends B {
- int da;
- Integer[] di = new Integer[3];
- }
- /**
- * 会算上A的实例字段
- */
- static class E extends A {
- int ea;
- int eb;
- }
- public static void main(String[] args) throws IllegalAccessException {
- System.out.println(new File("./target/classes").getAbsolutePath());
- System.out.println("sizeOf(new Object())=" + sizeOf(new Object()));
- System.out.println("sizeOf(new A())=" + sizeOf(new A()));
- System.out.println("sizeOf(new B())=" + sizeOf(new B()));
- System.out.println("sizeOf(new B2())=" + sizeOf(new B2()));
- System.out.println("sizeOf(new B[3])=" + sizeOf(new B[3]));
- System.out.println("sizeOf(new C())=" + sizeOf(new C()));
- System.out.println("fullSizeOf(new C())=" + fullSizeOf(new C()));
- System.out.println("sizeOf(new D())=" + sizeOf(new D()));
- System.out.println("fullSizeOf(new D())=" + fullSizeOf(new D()));
- System.out.println("sizeOf(new int[3])=" + sizeOf(new int[3]));
- System.out.println("sizeOf(new Integer(1)=" + sizeOf(new Integer(1)));
- System.out.println("sizeOf(new Integer[0])=" + sizeOf(new Integer[0]));
- System.out.println("sizeOf(new Integer[1])=" + sizeOf(new Integer[1]));
- System.out.println("sizeOf(new Integer[2])=" + sizeOf(new Integer[2]));
- System.out.println("sizeOf(new Integer[3])=" + sizeOf(new Integer[3]));
- System.out.println("sizeOf(new Integer[4])=" + sizeOf(new Integer[4]));
- System.out.println("sizeOf(new A[3])=" + sizeOf(new A[3]));
- System.out.println("sizeOf(new E())=" + sizeOf(new E()));
- }
- }
如果你是用maven打包的话,可以考虑。打完jar包后,可以直接运行SizeOfObject了,但是要加上vm启动参数(test.jar是刚才打的jar包):
- -javaagent:target/test.jar
在我64bit mac上,跑64位hotspot vm的结果如下,其中压缩对象指针参数是开启的,即-XX:+UseCompressedOops
- sizeOf(new Object())=16
- sizeOf(new A())=16
- sizeOf(new B())=24
- sizeOf(new B2())=24
- sizeOf(new B[3])=32
- sizeOf(new C())=24
- fullSizeOf(new C())=128
- sizeOf(new D())=32
- fullSizeOf(new D())=64
- sizeOf(new int[3])=32
- sizeOf(new Integer(1)=16
- sizeOf(new Integer[0])=16
- sizeOf(new Integer[1])=24
- sizeOf(new Integer[2])=24
- sizeOf(new Integer[3])=32
- sizeOf(new Integer[4])=32
- sizeOf(new A[3])=32
- sizeOf(new E())=24
如果关闭指针压缩,即在vm启动参数中加上-XX:-UseCompressedOops结果会不一样:
- sizeOf(new Object())=16
- sizeOf(new A())=24
- sizeOf(new B())=24
- sizeOf(new B2())=32
- sizeOf(new B[3])=48
- sizeOf(new C())=40
- fullSizeOf(new C())=160
- sizeOf(new D())=40
- fullSizeOf(new D())=88
- sizeOf(new int[3])=40
- sizeOf(new Integer(1)=24
- sizeOf(new Integer[0])=24
- sizeOf(new Integer[1])=32
- sizeOf(new Integer[2])=40
- sizeOf(new Integer[3])=48
- sizeOf(new Integer[4])=56
- sizeOf(new A[3])=48
- 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 !