美文网首页
如何查看java对象大小及其地址

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

作者: 全都是泡沫啦 | 来源:发表于2018-11-06 11:11 被阅读0次

解密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查看对象的大小

相关文章

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

    解密java对象的地址及大小: 一个对象的大小等于:对象头+属性占用空间(基础类型+引用类型 8Byte)关闭指针...

  • java反射-Field

    在之前的文章如何查看java对象大小及其地址中我们已经知道对象在创建的时候就已经知道该对象占用内存的大小,而对于去...

  • 查看java 对象的大小

    最近有点忙,上班烦了,所以抽时间去论坛看了看 ,看到一个用来显示对象大小的工具JOL (Java Object L...

  • 如何查看一个 Java 对象的大小

    最近一直着迷于缓存行,要想正确理解缓存行以及伪共享就一定要知道Java对象的大小,以及如何分布. 在这里演示了使用...

  • 如何计算Java对象所占内存的大小

    摘要 本文以如何计算Java对象占用内存大小为切入点,在讨论计算Java对象占用堆内存大小的方法的基础上,详细讨论...

  • 如何计算Java对象所占内存的大小

    摘要 本文以如何计算Java对象占用内存大小为切入点,在讨论计算Java对象占用堆内存大小的方法的基础上,详细讨论...

  • java对象大小

    java对象包括对小偷,实例数据和对齐填充 任何一个对象都有对象头,对象头在32位系统上占用,64位系统上占用16...

  • 关于centos7查看当前目录

    查看当前目录的容量 df -lh home/ 查看目录及其包含的文件的大小 du -ch directory 查看...

  • 关于Mac的基本开发环境

    如何查看Java版本 如何查看Java路径,直接执行命令/usr/libexec/java_home Java环境...

  • macOS 下 Java 环境的配置

    如何查看 Java 版本? 如何查看 Java Home 目录? 如何 JDK 安装? 使用 brew 安装 or...

网友评论

      本文标题:如何查看java对象大小及其地址

      本文链接:https://www.haomeiwen.com/subject/mcgyxqtx.html