码迷,mamicode.com
首页 > 其他好文 > 详细

利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值

时间:2018-01-18 13:19:05      阅读:273      评论:0      收藏:0      [点我收藏+]

标签:tar   body   array   dbase   track   new   ack   port   实例   

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

 

  1. public class VO  
  2. {  
  3.     public int a = 0;  
  4.       
  5.     public long b = 0;  
  6.       
  7.     public static String c= "123";  
  8.       
  9.     public static Object d= null;  
  10.       
  11.     public static int e = 100;  
  12. }  


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

 

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


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

 

  1. // 数组第一个元素的偏移地址,即数组头占用的字节数  
  2. int[] intarr = new int[0];  
  3. System.out.println(unsafe.arrayBaseOffset(intarr.getClass()));  
  4.   
  5. // 数组中每个元素占用的大小  
  6. 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.获取类的静态字段偏移

 

  1. // 获取类的静态字段偏地址  
  2. System.out.println(unsafe.staticFieldOffset(VO.class.getDeclaredField("c")));  
  3. System.out.println(unsafe.staticFieldOffset(VO.class.getDeclaredField("d")));  
  4.   
  5. // 获取静态字段的起始地址,通过起始地址和偏移地址,就可以获取静态字段的值了  
  6. // 只不过静态字段的起始地址,类型不是long,而是Object类型  
  7. Object base1 = unsafe.staticFieldBase(VO.class);  
  8. Object base2 = unsafe.staticFieldBase(VO.class.getDeclaredField("d"));  
  9. System.out.println(base1==base2);//true  


4.获取操作系统的位数

 

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



通过上面的几段代码,我们可以成功获取类中各个字段的偏移地址,这跟jol工具的输出结果和我们的结论是一致的。有了字段的偏移地址,在加上对象的起始地,我们就能够通过Unsafe直接获取字段的值了。


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

 

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


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

 

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


可以看到Unsafe功能是很强大的,位java语言提供了更底层的功能。

利用sun.misc.Unsafe获取类字段的偏移地址和读取字段的值

标签:tar   body   array   dbase   track   new   ack   port   实例   

原文地址:https://www.cnblogs.com/hanguocai/p/8309406.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!