如何查看java对象大小及其地址

解密java对象的地址及大小:

操作系统:
  windows 64 
java运行环境:
  java version "1.8.0_40"
  Java(TM) SE Runtime Environment (build 1.8.0_40-b25)
  Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)
main启动参数
  vm options:-XX:-UseCompressedOops  关闭指针压缩
import sun.misc.Unsafe;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;

/**
 * @author paul
 * @description
 * @date 2018/11/5
 */
public class Test2 {

    public int a;
    public int b;
    public int c;

    public Object o1;
    public Object o2 = new TestObjectSize();
    public Object o3;

    public int[] int1;
    public int[] int2;
    public int[] int3;

    public char char1;
    public char char2;
    public char char3;

    public static void main(String[] args) throws Exception {
        String name = ManagementFactory.getRuntimeMXBean().getName();
        System.out.println(name);
        //操作系统:windows 64位
        //关闭指针压缩-XX:-UseCompressedOops
        Unsafe unsafeInstance = getUnsafeInstance();
        {
            //普通对象头大小:16Byte.
            //                8:Mark Word + 8:Class Pointer Class对象(其对应的元数据对象)的内存地址
            long testObjectSizeOffset = unsafeInstance.objectFieldOffset(TestObjectSize.class.getDeclaredField("flag")); //16
            //testObjectSizeOffset为字段flag对于对象TestObjectSize的偏向地址,则说明对象头占用了16字节
            int flag = unsafeInstance.getInt(new TestObjectSize(), testObjectSizeOffset);//100
            //字段flag为基础类型int占用4字节,则getInt取到flag的值100
        }
        {
            //数组对象头大小:24Byte:(8+8+4)
            //                8:Mark Word + 8:Class Pointer Class对象(其对应的元数据对象)的内存地址 + 4:数组长度
            int[] ints = new int[200];
            long IntArrayOffset = unsafeInstance.arrayBaseOffset(int[].class);//24 数组对象头的大小
            int intsCount = unsafeInstance.getInt(ints, 16L);//200 ints数组的大小
        }

        //注意在对象内存分布时,基础类型和引用类型(8Byte:为对象的地址)会在内存中分开,如果所有基础类型的size不是8的倍数则对齐填充
        //下面可以展示对象填充(char3从偏移量32到34,o1偏移量为40,40-34=6填充6Byte)
        Test2 test2 = new Test2();

        {
            long aOffset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("a"));//16
            long bOffset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("b"));//20
            long cOffset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("c"));//24
            long o1Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("o1"));//40
            long o2Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("o2"));//48
            long o3Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("o3"));//56
            long int1Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("int1"));//64
            long int2Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("int2"));//72
            long int3Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("int3"));//80
            long char1Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("char1"));//28
            long char2Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("char2"));//30
            long char3Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("char3"));//32
        }
        //如何获取Test2中字段o2引用对象(TestObjectSize)的flag字段:
        long o2Offset = unsafeInstance.objectFieldOffset(Test2.class.getDeclaredField("o2"));//48
        //os为对象引用占8字节存放对象地址
        long o2Address = unsafeInstance.getLong(test2, o2Offset);//此处为Test2中o2对象的地址

        //TestObjectSize为普通对象,则跳过16Byte后的4字节存放int
        int unsafeInstanceInt = unsafeInstance.getInt(o2Address + 16);
        assert unsafeInstanceInt == ((TestObjectSize) test2.o2).flag;

        System.out.println("test2地址   :" + getObjectAddress(test2));
        System.out.println("test2.o2地址:" + getObjectAddress(test2.o2));

        System.out.println("test2地址   :" + getObjectAddress(test2));
        System.out.println("test2.o2地址:" + getObjectAddress(test2.o2));
        System.out.println("test2地址   mark  init:" + getObjectMarkValue(test2));

        synchronized (test2) {
            System.out.println("test2地址   mark  lock:" + getObjectMarkValue(test2));
        }
        System.out.println("...");
    }

    public static Unsafe getUnsafeInstance() throws Exception {
        Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafeInstance.setAccessible(true);
        return (Unsafe) theUnsafeInstance.get(Unsafe.class);
    }

    public static class TestObjectSize {
        public int flag = 100;
    }

    /**
     * 获取目标对象头mark值
     *
     * @param o 目标对象
     * @return 目标对象地址
     */
    public static Long getObjectMarkValue(Object o) {

        try {
            Unsafe unsafeInstance = getUnsafeInstance();
            return unsafeInstance.getLong(o, 0L);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取目标对象头类型对象地址
     *
     * @param o 目标对象
     * @return 类型对象地址
     */
    public static Long getObjectClassValue(Object o) {

        try {
            Unsafe unsafeInstance = getUnsafeInstance();
            return unsafeInstance.getLong(o, 8L);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取目标对象的内存地址 10进制
     *
     * @param o 目标对象
     * @return 目标对象地址
     */
    public static Long getObjectAddress(Object o) {

        ObjectAddressHolder objectAddressHolder = new ObjectAddressHolder();
        objectAddressHolder.object = o;
        try {
            Unsafe unsafeInstance = getUnsafeInstance();
            long objectFieldOffset = unsafeInstance.objectFieldOffset(ObjectAddressHolder.class.getDeclaredField("object"));
            long objectAddress = unsafeInstance.getLong(objectAddressHolder, objectFieldOffset);
            objectAddressHolder.object = null;
            objectAddressHolder = null;
            return objectAddress;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static class ObjectAddressHolder {
        public Object object;
    }
}

一个对象的大小等于:对象头+属性占用空间(基础类型+引用类型 8Byte)
关闭指针压缩:Test2大小=16(对象头) + 4(int)3 + 8(Object)3 + 8(int[])3 + 2(char)3

验证方式:jdk自带工具:java -classpath "%JAVA_HOME%/lib/sa-jdi.jar" sun.jvm.hotspot.HSDB
或者使用:使用Instrumentation查看对象的大小

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一场大雨 让我迷了路 三叉路口 空寂而孤独 我选择了一条看似熟悉的路 走了很久 才发现找不到归途 我顿时迷糊 是继...
    徐影辞阅读 1,384评论 0 0
  • 周期2017.1.16-1.31 下半月 2017年一月下半月读完了四本小说,分别是《夏目友人帐》,《法医密档:不...
    Samhanx阅读 2,444评论 2 1
  • 作者:奕畅【原创作品】24 我出生在太极之乡---燕赵之都邯郸,杨式太极,武式太极的发源地。跟着师傅学拳有一段时间...
    奕畅阅读 3,077评论 0 1