您的位置:首页 > 编程语言 > Java开发

Java对象到底多大?

2016-10-31 23:08 204 查看
           很多时候我们想知道一个Java对象到底有多大, 但Java没有像C语言那样提供一个类似于sizeof的方法。作为Java老司机的你,是否考虑过这个问题?

     在《深入理解Java虚拟机》这边书中提到HotSpot虚拟机是8字节对齐的, 一个Java对象包括对象头(Header),实例数据(Instance Data)和对齐填充(Padding), 现在的HotSpot虚拟机一般运行在64位电脑上。

        不管是否压缩,即UseCompressOops。 Java对象占用字节数都是8的整数倍,8的整数倍,8的整数倍!!! 重要的事情说3遍。

          Object obj  = new Object();  请问在这句代码使用了多大内存?   不考虑压缩(XX:-UseCompressedOops),答案是24个字节, 其中引用obj在Java栈里使用8个字节, new Object在Java堆里使用16个字节。 

      Java Instrumentation类的getObjectToSize方法可以测量Java对象的大小, 要使用javagent注入的方式得到Instrumentation的引用。

/**
* Returns an implementation-specific approximation of the amount of storage consumed by
* the specified object. The result may include some or all of the object's overhead,
* and thus is useful for comparison within an implementation but not between implementations.
*
* The estimate may change during a single invocation of the JVM.
*
* @param objectToSize     the object to size
* @return an implementation-specific approximation of the amount of storage consumed by the specified object
* @throws java.lang.NullPointerException if the supplied Object is <code>null</code>.
*/
long getObjectSize(Object objectToSize);


        我总结为表格, 方法理解


Java语言有8种原生数据类型, 即8种类型关键字。原生类型(primitive type)的内存占用如下:

           类型                                         内存占用(字节)
boolean1                                         
byte1
short2
char2
int4
float4
long8
double8
            以上的8种数据类型压缩前后占用空间大小不变, 在32位/64位机器上占用空间一致, 即Java是平台无关的。

 下边是我总结的计算方法:

                                                  对象大小:(对象头 + 实例数据 + padding)%8等于0, padding是补齐成8的整数倍

                                                                         下面各个对象的大小都是按照上述顺序计算的。
对象            已压缩(字节)             -XX:+UseCompressedOops                    未压缩(字节)                 -XX:-UseCompressedOops说明
new Object()12+Padding=1616压缩前对象头占16个字节,压缩后占12个字节。
  static class A {  

        int a;     }                            new A()
12+4=16

12是对象头,4是int型大小
16+4+Padding=24

16是对象头大小,4是int型大小
对象头压缩前占16个字节,压缩后占12个字节;int型占4个字节。
 static class B {  

        int a;  

        int b;   }                          new B()  
12+4+4+Padding=24

12是对象头,第一个4是变量a,第二个4是变量b,  paddinb是余8补齐的4.
16+4+4=24区别是已压缩要添加8字节对齐的Padding。
  static class B2 {  

        int b2a;  

       Integer b2b;  

    }                                new B2()
12+4+4=24

12是对象头,第一个4是变量b2a,第二个4是b2b引用。
16+4+8+Padding=32在64位机器上引用压缩前占8个字节,压缩后占4个字节。
new Object[3]16+3*4+Padding=32

16是对象头,4是单个引用大小。
24+8*3=48

24是对象头,8是单个引用大小
数组对象的对象头在压缩前占24个字节,压缩后占16个字节。
new Object[1]16+1*4+padding=24

16是对象头,4是引用大小;
24+1*8=32

24是对象头,8是单个引用大小
 
单一对象头1216 
数组对象头1624 
引用48 
              有兴趣的可以下载我用 IntelliJ写的demo工程:http://download.csdn.net/detail/brycegao321/9670233

1、新建Java工程;







2、设置输出为jar包, 得到MANIFEST.MF文件。



 


  选中 Build on make选项。



           修改MANIFEST.MF,  注意Boot-Class-Path后面有个空格!!!



MANIFEST.MF要添加下面三行
Premain-class: SizeUtils      (实现premain方法,得到Instrumentation的引用)
Can-Redefine-Classes: false
Boot-Class-Path:        (注意:Path:后面有空格,否则编译不过!!!)


编译后生成jar包。

测试代码:

public class SizeTest {
/**
* -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("sizeOf(new Object())=" + SizeUtils.sizeOf(new Object()));
System.out.println("sizeOf(new A())=" + SizeUtils.sizeOf(new A()));
System.out.println("sizeOf(new B())=" + SizeUtils.sizeOf(new B()));
System.out.println("sizeOf(new B2())=" + SizeUtils.sizeOf(new B2()));
System.out.println("sizeOf(new B[3])=" + SizeUtils.sizeOf(new B[3]));
System.out.println("sizeOf(new C())=" + SizeUtils.sizeOf(new C()));
System.out.println("fullSizeOf(new C())=" + SizeUtils.fullSizeOf(new C()));
System.out.println("sizeOf(new D())=" + SizeUtils.sizeOf(new D()));
System.out.println("fullSizeOf(new D())=" + SizeUtils.fullSizeOf(new D()));
System.out.println("sizeOf(new int[3])=" + SizeUtils.sizeOf(new int[3]));
System.out.println("sizeOf(new Integer(1)=" + SizeUtils.sizeOf(new Integer(1)));
System.out.println("sizeOf(new Integer[0])=" + SizeUtils.sizeOf(new Integer[0]));
System.out.println("sizeOf(new Integer[1])=" + SizeUtils.sizeOf(new Integer[1]));
System.out.println("sizeOf(new Integer[2])=" + SizeUtils.sizeOf(new Integer[2]));
System.out.println("sizeOf(new Integer[3])=" + SizeUtils.sizeOf(new Integer[3]));
System.out.println("sizeOf(new Integer[4])=" + SizeUtils.sizeOf(new Integer[4]));
System.out.println("sizeOf(new A[3])=" + SizeUtils.sizeOf(new A[3]));
System.out.println("sizeOf(new E())=" + SizeUtils.sizeOf(new E()));
System.out.println("sizeOf(new HashMap)=" + SizeUtils.sizeOf(new HashMap<String,Object>()));
}
}


输出:   左边是没压缩, 右边是压缩的, 单位是字节。 注意cmd命令啊!

java  -javaagent:CalcSize.jar -XX:+UseCompressedOops -jar CalcSize.jar 

java  -javaagent:CalcSize.jar -XX:-UseCompressedOops -jar CalcSize.jar



参考:
http://www.jroller.com/maxim/entry/again_about_determining_size_of http://yueyemaitian.iteye.com/blog/2033046 http://www.importnew.com/14948.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: