1,打包項目 把項目打成jar 2,配置idea遠程調試 我設置的是本地調試,遠程伺服器設置為遠程的伺服器和埠即可。 3, 伺服器啟動項目 啟動項目: java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 - ...
在一些常用的中間件中,我們經常看見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