java对象包含哪些内容,对象大小怎么算

本文旨在说明java对象的内存布局,然后基于此说明如何计算一个对象占用的字节数

java对象的内存布局分为三部分,对象头、实例数据、对齐补白

对象头:

      1.mark word,包含了对象的hash值,GC分代年龄,锁状态,线程持有的锁,偏向锁等信息

      2.指向当前对象所属类的Class对象指针,通过它可以获取class信息,这个指针是反射的基础

      3.如果是数组,还必须包括数据的长度信息

对象头的长度:这里只说64位操作系统

开启了UseCompressedOops指针压缩的(默认开启):

      普通对象头:12byte

      数组对象头:16byte

关闭了UseCompressedOops的:

      普通对象头:16byte

      数组对象头:24byte

实例数据:

      接下来就是实例数据,也就是对象成员变量的内容,包括了从父类继承下来的内容

 

对齐补白:

      jvm要求java对象的起始地址必须是8的倍数,如果对象头+实例长度不足8的倍数,就需要补齐

 

指针的长度:

      32位操作系统和64位操作系统的一个主要区别是内存地址空间范围不一样,最大值分别为2的32次方、2的64次方,相应的,如果想表示内存地址,分别需要32位和64位,在开发语言中,一个引用其实就是一个地址,在java中,我们通过对象引用来访问对象,对象引用就是一个地址;比如:Object obj=new Object(); 这里obj就是一个地址,在32位操作系统中,obj这个引用占32位,也就是4byte,64位操作系统中是8byte,不过64位操作系统下的jvm会默认开启指针压缩功能,将8byte压缩为4byte

 

基本类型的长度:

boolean 1byte;  byte 1byte;

short 2byte;  char 2byte;

int 4byte;  float 4byte;

double 8byte;  long 8byte;

 

好了,有了上面的铺垫,我们就可以进行一些简单对象大小的计算了(64位操作系统,开启了UseCompressedOops指针压缩):

class A{

}

new A();占用多少字节?12(对象头)+4(不足8的倍数,补齐)=16byte;

 

class B{

      private int i;

      public B(int i){this.i=i;}

}

new B(1);占用多少字节?12(对象头)+4(int型成员变量)=16byte;

 

class C{

      private int i;

      private B b;

      public C(int i,B b){

              this.i=i;

              this.b=b;

      }

}

new C(1,new B());占用多少字节?12(对象头)+4(成员变量i)+4(成员变量b引用)+4(不足8的倍数,补齐)=24byte;

通过这种方式,只要不是过于复杂的对象,我们都可以大致推算出它所占的内存了,不过对于复杂的对象,就没那么好算了,我们当然希望有更好的方法了.....

java.lang.instrument.Instrumentation提供了getObjectSize()方法,可以计算任何一个运行时对象的大小,由于这个类的实现类InstrumentationImpl的构造方法是私有的,所以我们创建不了其实例,不过jvm可以替我们创建,需要依赖于javaagent代理机制,使用javaagent,我们要先构造一个java类,在其中实现static void premain(String args, Instrumentation inst)方法,jvm会在启动时调用这个premain方法,并传入一个Instrumentation实例

代理类:

public class SizeCalcuater {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

然后是测试类:

public class Tester {

    public static void main(String[] args) {
        System.out.println("new A()大小:" + SizeCalcuater.getObjectSize(new A()));
        System.out.println("new B(1)大小:" + SizeCalcuater.getObjectSize(new B(1)));
        System.out.println("new C(1, new B(1))大小:" + SizeCalcuater.getObjectSize(new C(1, new B(1))));
    }

    static class A {
    }

    static class B {
        private final int i;

        public B(int i) {
            this.i = i;
        }
    }

    static class C {
        private final int i;
        private final B b;

        public C(int i, B b) {

            this.i = i;

            this.b = b;
        }
    }
}

然后,要指定MANIFEST.MF文件用于打包:

在src目录下新建META-INF目录,在META-INF目录下建两个MANIFEST.MF文件(注意,:后空一格,最后必须有个空行)

MANIFEST1.MF(注意,:后空一格,最后必须有个空行)

Manifest-Version: 1.0
Premain-Class: com.bobo.size.SizeCalcuater
 

MANIFEST2.MF(注意,:后空一格,最后必须有个空行)

Manifest-Version: 1.0
Main-Class: com.bobo.size.Tester
 

然后分别用这两个MANIFEST.MF文件打jar包,我这里两个jar包分别叫size.jar、test.jar

运行test.jar,并指定javaagent代理包:

java -javaagent:size.jar -jar test.jar

输出如下结果:符合我们上面的推论..

 

另外,我们还可以使用图形化工具查看堆dump文件来确定对象大小,VisualVM和MAT都可以,这里就不再展示了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值