在一些常用的中間件中,我們經常看見Unsafe類(sun.misc.Unsafe)的使用,如Netty、Cassandra、Hadoop、Kafka等,那么你知道Unsafe類的功能嗎?
下文筆者將一一道來,如下所示:
Unsafe類的功能簡介
Unsafe類在sun.misc包下
Unsafe類不是Java標準類,一般的開發者不會涉及此類的開發
Unsafe類可提高java的運行效率
Unsafe類的功能:
使我們可跳過JVM,使java語言擁有c語言指標一樣的能力,
如:操作記憶體空間,CAS,并發編程等能力
Unsafe類簡介
Unsafe類使用“final”修改,不可以繼承,并且建構式是private,所以我們只能使用靜態方法獲取它的實體
例:
private Unsafe() {
}
?
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
從getUnsafe()方法中,我們可以得知,
只有主類加載器才可以呼叫此方法,否則就會拋出例外
例:呼叫getUnsafe方法回傳Unsafe
public static Unsafe getUnsafe() throws IllegalAccessException {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
return (Unsafe) unsafeField.get(null);
}
Unsafe的主要功能

記憶體管理
Unsafe的記憶體管理功能主要包括 普通讀寫、volatile讀寫、有序寫入、直接操作記憶體等分配記憶體與釋放記憶體的功能
普通讀寫
Unsafe可以讀寫一個類的屬性
即使這個屬性是私有的
也可以對這個屬性進行讀寫,
//獲取記憶體地址指向的整數
public native int getInt(Object var1, long var2);
getInt用于從物件的指定偏移地址處讀取一個int
?
//將整數寫入指定記憶體地址
public native void putInt(Object var1, long var2, int var4);
putInt用于在物件指定偏移地址處寫入一個int,其他原始型別也提供有對應的方法
另:Unsafe的getByte、putByte方法提供了直接在一個地址上進行讀寫的功能
volatile讀寫
普通的讀寫無法保證可見性和有序性,而volatile讀寫就可以保證可見性和有序性,
// 獲取記憶體地址指向的整數,并支持volatile語意
public native int getIntVolatile(Object var1, long var2);
?
// 將整數寫入指定記憶體地址,并支持volatile語意
public native void putIntVolatile(Object var1, long var2, int var4);
volatile讀寫要保證可見性和有序性,相對普通讀寫更加昂貴
有序寫入
有序寫入只保證寫入的有序性,不保證可見性
就是說一個執行緒的寫入不保證其他執行緒立馬可見,
// 將整數寫入指定記憶體地址、有序或者有延遲的方法
public native void putOrderedInt(Object var1, long var2, int var4);
而與volatile寫入相比putOrderedXX寫入代價相對較低
putOrderedXX寫入不保證可見性但
是保證有序性,所謂有序性,就是保證指令不會重排序,
直接操作記憶體
Unsafe提供了直接操作記憶體的能力:
// 分配記憶體
public native long allocateMemory(long var1);
// 重新分配記憶體
public native long reallocateMemory(long var1, long var3);
// 記憶體初始化
public native void setMemory(long var1, long var3, byte var5);
// 記憶體復制
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
// 清除記憶體
public native void freeMemory(long var1);
?
對應操作記憶體,也提供了一些獲取記憶體資訊的方法:
// 獲取記憶體地址
public native long getAddress(long var1);
?
public native int addressSize();
?
public native int pageSize();
注意事項:
使用copyMemory方法可以實作一個通用的物件拷貝方法
無需再對每一個物件都實作clone方法,但只能做到物件淺拷貝
"非常規”物件實體化
通常情況下,我們使用new關鍵字實體化一個物件,但是Unsafe類中有一個方法allocateInstance,可直接生成物件實體,無需構造方法和其它初始化方法
// 直接生成物件實體,不會呼叫這個實體的構造方法 public native Object allocateInstance(Class<?> var1) throws InstantiationException;
類加載
通過以下方法,可以實作類的定義、創建等操作, // 方法定義一個類,用于動態地創建類 public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6); // 動態的創建一個匿名內部類 public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3); // 判斷是否需要初始化一個類 public native boolean shouldBeInitialized(Class<?> var1); // 保證已經初始化過一個類 public native void ensureClassInitialized(Class<?> var1);
偏移量相關
Unsafe提供以下方法獲取物件的指標,通過對指標進行偏移,不僅可以直接修改指標指向的資料(即使它們是私有的),甚至可以找到JVM已經認定為垃圾、可以進行回收的物件, // 獲取靜態屬性Field在物件中的偏移量,讀寫靜態屬性時必須獲取其偏移量 public native long staticFieldOffset(Field var1); // 獲取非靜態屬性Field在物件實體中的偏移量,讀寫物件的非靜態屬性時會用到這個偏移量 public native long objectFieldOffset(Field var1); // 回傳Field所在的物件 public native Object staticFieldBase(Field var1); // 回傳陣列中第一個元素實際地址相對整個陣列物件的地址的偏移量 public native int arrayBaseOffset(Class<?> var1); // 計算陣列中第一個元素所占用的記憶體空間 public native int arrayIndexScale(Class<?> var1);
陣列操作
陣列操作提供了以下方法: // 獲取陣列第一個元素的偏移地址 public native int arrayBaseOffset(Class<?> var1); // 獲取陣列中元素的增量地址 public native int arrayIndexScale(Class<?> var1);
執行緒調度
執行緒調度相關方法如下: // 喚醒執行緒 public native void unpark(Object var1); // 掛起執行緒 public native void park(boolean var1, long var2); // 用于加鎖,已廢棄 public native void monitorEnter(Object var1); // 用于加鎖,已廢棄 public native void monitorExit(Object var1); // 用于加鎖,已廢棄 public native boolean tryMonitorEnter(Object var1); 使用park方法將執行緒進行掛起, 執行緒將一直阻塞到超時或中斷條件出現 使用unpark方法可以終止一個掛起的執行緒,使其恢復正常
CAS操作
Unsafe類的CAS操作可能是使用最多的方法 它為Java的鎖機制提供了一種新的解決辦法, 如AtomicInteger等類都是通過該方法來實作的 compareAndSwap方法是原子的,可以避免繁重的鎖機制,提高代碼效率 public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6); CAS一般用于樂觀鎖 它在Java中有廣泛的應用,ConcurrentHashMap,ConcurrentLinkedQueue中都有用到CAS來實作樂觀鎖,
記憶體屏障
JDK8新引入了用于定義記憶體屏障、避免代碼重排的方法: // 保證在這個屏障之前的所有讀操作都已經完成 public native void loadFence(); // 保證在這個屏障之前的所有寫操作都已經完成 public native void storeFence(); // 保證在這個屏障之前的所有讀寫操作都已經完成 public native void fullFence();
其他
當然,Unsafe類中還提供了大量其他的方法
如上面提到的CAS操作,以AtomicInteger為例
當我們呼叫getAndIncrement、getAndDecrement等方法時
本質上呼叫的就是Unsafe的getAndAddInt方法
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
轉自:http://www.java265.com/JavaCourse/202204/2835.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/457986.html
標籤:其他
上一篇:idea遠程debug
