在上一篇文章中。我们列出了计算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"));
有了字段的偏移地址,在加上对象的起始地。我们就行通过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语言提供了更底层的功能。