Java中的Unsafe類
Java中的Unsafe類
Unsafe類時sun,misc包下的一個類,主要用于執行一些 Native 方法,同時它賦予了 Java 直接操作內存的能力,但是直接操作內存的能力同時也破壞了Java的安全性,可能這就是為什么叫 Unsafe 吧
關于Native 方法
Native 方法是沒有方法體的,是為了調用其他語言寫的( C/C++ )代碼而寫的函數,如果了解 JVM 的話,就知道線程內部不僅有虛擬機棧,還有一個本地方法棧,虛擬機棧是用來存放非本地方法調用時的棧幀,本地方法棧就是存放本地方法調用時的棧幀
Unsafe 獲取
Unsafe類中定義了一個 getUnsafe() 方法,但是如果直接調用會拋出如下異常
Exception in thread "main" java.lang.SecurityException: Unsafe
這是因為 Unsafe 源碼中 getUnsafe() 方法是如下定義
@CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}
// 可以看到會先判斷是不是系統類加載器調用的該方法,否則拋出SecurityException異常
直接獲取不了只能使用反射的方法獲取
在 Unsafe 類中定義了如下變量 ( theUnsafe )
private static final Unsafe theUnsafe = new Unsafe();
所以可以直接使用如下方式獲取到 Unsafe 類對象
private static Unsafe unsafe;
private void init() throws NoSuchFieldException, IllegalAccessException {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
}
Unsafe 中常用的方法
//分配新的本地空間
public native long allocateMemory(long bytes);
//重新調整內存空間的大小
public native long reallocateMemory(long address, long bytes);
//將內存設置為指定值
public native void setMemory(Object o, long offset, long bytes, byte value);
//內存拷貝
public native void copyMemory(Object srcBase, long srcOffset,Object destBase, long destOffset,long bytes);
//清除內存
public native void freeMemory(long address);
// 獲取對象屬性偏移量
public native long objectFieldOffset(Field field);
// 獲取對象靜態屬性偏移量
public native long staticFieldOffset(Field field);
// 獲取靜態對象的基址
public Object staticFieldBase(Field field);
//獲取數組的基址
public native int arrayBaseOffset(Class<?> arrayClass);
//獲取數組的元素占用大小
public native int arrayIndexScale(Class<?> arrayClass);
// 比較并更新一個Object屬性(CAS操作)
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object update);
public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);
...
練習
給一個題目:輪轉數組
給定一個數組,將數組中的元素向左移動 k 個位置,其中 k 是非負數。
輸入: nums = [1,2,3,4,5,6,7], k = 3
輸出: [4,5,6,7,1,2,3]
關于這一題其實有多種解法,但是這里主要嘗試使用Unsafe類來解這個題
給定如下思路:
創建一個nums二倍長度的數組,存放兩次nums中的數
例:
nums = {1,2,3,4,5,6}, k = 3
// 兩倍數組并賦值后
nums2 = {1,2,3,4,5,6,1,2,3,4,5,6}
// 現在要得到目標數組,只需要從 nums2 中的第 k + 1 個元素開始截取,即可獲得
nums2 => {1,2,3, | 4,5,6,1,2,3 | 4,5,6} => {4,5,6,1,2,3}
實現如下:
public int[] solution(int[] nums,int k) throws IllegalAccessException, NoSuchFieldException {
// 擴容后的數組
int[] nums2 = new int[nums.length * 2];
int len = nums.length;
// 利用反射獲取 Unsafe 類
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
// 獲取int[]的偏移地址
int offset = unsafe.arrayBaseOffset(int[].class);
// 獲取int[]中每個元素的大小
int size = unsafe.arrayIndexScale(int[].class);
// 將nums中的元素內容復制到nums2中,復制兩次讓nums2 為兩次 nums連接
unsafe.copyMemory(nums,offset,nums2,offset,size * len);
unsafe.copyMemory(nums,offset, nums2 ,offset + size * len, size * len);
// 從第 k 個元素開始取 len 個元素復制回 nums
unsafe.copyMemory(nums2,offset + k * size,nums, offset ,size * len);
return nums;
}
浙公網安備 33010602011771號