利用sun.misc.Unsafe獲取類字段的偏移地址和讀取字段的值
阿新 • • 發佈:2018-01-18
com 如何 string ring rep 最好 lar 計算 .get
我們列出了計算java對象大小的幾個結論以及jol工具的使用,jol工具的源碼有興趣的可以去看下。現在我們利用JDK中的sun.misc.Unsafe來計算下字段的偏移地址,一則驗證下之前文章中的結論,再則跟jol輸出結果對比下。如何獲取sun.misc.Unsafe對象,可以參考這篇文章。
[java] view plain copy
- 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.獲取實例字段的偏移地址
[java] view plain copy
- // 獲取實例字段的偏移地址,偏移最小的那個字段(僅挨著頭部)就是對象頭的大小
- 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.獲取數組的頭部大小和元素大小
[java] view plain copy
- // 數組第一個元素的偏移地址,即數組頭占用的字節數
- 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.獲取類的靜態字段偏移
[java] view plain copy
- // 獲取類的靜態字段偏地址
- 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.獲取操作系統的位數
[java] view plain copy
- // 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.讀取對象實例字段的值
[java] view plain copy
- //獲取實例字段的屬性值
- 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.獲取靜態字段的屬性值
[java] view plain copy
- 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語言提供了更底層的功能。
利用sun.misc.Unsafe獲取類字段的偏移地址和讀取字段的值