zoukankan      html  css  js  c++  java
  • java对象的内存布局(二):利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值

    上一篇文章中。我们列出了计算java对象大小的几个结论以及jol工具的使用,jol工具的源代码有兴趣的能够去看下。如今我们利用JDK中的sun.misc.Unsafe来计算下字段的偏移地址,一则验证下之前文章中的结论,再则跟jol输出结果对照下。怎样获取sun.misc.Unsafe对象。能够參考这篇文章

    public class VO
    {
    	public int a = 0;
    	
    	public long b = 0;
    	
    	public static String c= "123";
    	
    	public static Object d= null;
    	
    	public static int e = 100;
    }

    1.获取实例字段的偏移地址

    // 获取实例字段的偏移地址,偏移最小的那个字段(仅挨着头部)就是对象头的大小
    System.out.println(unsafe.objectFieldOffset(VO.class.getDeclaredField("a")));
    System.out.println(unsafe.objectFieldOffset(VO.class.getDeclaredField("b")));
    
    // fieldOffset与objectFieldOffset功能一样,fieldOffset是过时方法,最好不要再使用
    System.out.println(unsafe.fieldOffset(VO.class.getDeclaredField("b")));


    2.获取数组的头部大小和元素大小

    // 数组第一个元素的偏移地址,即数组头占用的字节数
    int[] intarr = new int[0];
    System.out.println(unsafe.arrayBaseOffset(intarr.getClass()));
    
    // 数组中每一个元素占用的大小
    System.out.println(unsafe.arrayIndexScale(intarr.getClass()));
    Unsafe类中有非常多以BASE_OFFSET结尾的常量,比方ARRAY_INT_BASE_OFFSET等,这些常量值是通过arrayBaseOffset方法得到的。arrayBaseOffset方法是一个本地方法,能够获取数组第一个元素的偏移地址。Unsafe类中还有非常多以INDEX_SCALE结尾的常量,比方 ARRAY_INT_INDEX_SCALE 等。这些常量值是通过arrayIndexScale方法得到的。将arrayBaseOffset与arrayIndexScale配合使用,能够定位数组中每一个元素在内存中的位置。

    3.获取类的静态字段偏移

    // 获取类的静态字段偏地址
    System.out.println(unsafe.staticFieldOffset(VO.class.getDeclaredField("c")));
    System.out.println(unsafe.staticFieldOffset(VO.class.getDeclaredField("d")));
    
    // 获取静态字段的起始地址,通过起始地址和偏移地址,就能够获取静态字段的值了
    // 仅仅只是静态字段的起始地址,类型不是long,而是Object类型
    Object base1 = unsafe.staticFieldBase(VO.class);
    Object base2 = unsafe.staticFieldBase(VO.class.getDeclaredField("d"));
    System.out.println(base1==base2);//true

    4.获取操作系统的位数

    //  Report the size in bytes of a native pointer.
    //  返回4或8,代表是32位还是64位操作系统。
    System.out.println(unsafe.addressSize());
    // 返回32或64,获取操作系统是32位还是64位
    System.out.println(System.getProperty("sun.arch.data.model"));



    通过上面的几段代码。我们可以成功获取类中各个字段的偏移地址,这跟jol工具的输出结果和我们的结论是一致的。

    有了字段的偏移地址,在加上对象的起始地。我们就行通过Unsafe直接获取字段的值了。


    5.读取对象实例字段的值

    //获取实例字段的属性值
    VO vo = new VO();
    vo.a = 10000;
    long aoffset = unsafe.objectFieldOffset(VO.class.getDeclaredField("a"));
    int va = unsafe.getInt(vo, aoffset);
    System.out.println("va="+va);

    6.获取静态字段的属性值

    VO.e = 1024;
    Field sField = VO.class.getDeclaredField("e");
    Object base = unsafe.staticFieldBase(sField);
    long offset = unsafe.staticFieldOffset(sField);
    System.out.println(unsafe.getInt(base, offset));//1024

    能够看到Unsafe功能是非常强大的,位java语言提供了更底层的功能。


  • 相关阅读:
    java笔记6 Java语言基本类; String和StringBuffer ;集合类;流,文件及文本应用:JDBC
    java笔记5
    java笔记4 异常处理
    java笔记3
    java课笔记2
    java课笔记1
    TEXT
    机器学习
    WordCountPro
    WCProject(java)
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5207393.html
Copyright © 2011-2022 走看看