通常Java代碼都是運行在JVM中而不能直接訪問系統硬件如進行內存分配釋放等,但如果有需要跳過JVM直接用Java訪問系統硬件,比如像C語言指針一樣操作的話就可以調用Unsafe對象相關方法。
1、Unsafe說明
Unsafe類在sun.misc包下,不屬於Java標准。但是很多Java基礎類庫,包括一些高性能的開發庫都是基於Unsafe類開發的,比如Netty、Hadoop、Kafka、JUC並發包的基礎AQS依賴的CAS操作和LockSupport類等。Unsafe在提升Java運行效率,增強Java底層操作能力方面起了很大的作用。但它大部分操作都是繞過JVM通過JNI完成的,因此它分配的內存需要手動free,過度的使用Unsafe類會讓出錯幾率變大,所以是非常危險的,Java官方不建議直接使用。
2、Unsafe類的使用
Unsafe類是一個單例,可通過getUnsafe方法獲取,但內部會檢查限制,只有是系統類加載器加載的類才能調用,否則會拋出異常。可通過JVM啟動參數-Xbootclasspath指定要啟動的類為啟動類或通過反射獲取到它的實例【Unsafe類中有一個私有的成員變量theUnsafe,我們可以用反射將其實例的訪問屬性設置為true,然后通過File的get方法獲取】。
Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null);
獲取到Unsafe的實例之后,我們就可以實現如下功能
- 內存管理:內存的分配、釋放、拷貝等,利用copyMemory方法可以實現一個通用的對象淺拷貝方法。
- 非常規對象的實例化:使用allocateInstance方法能直接生成實例對象,無需調用構造方法和它的初始化方法,在對象反序列化時會很有用,能重建和設置final字段而不要調用構造方法。
- 操作類、對象、變量:獲取對象指針,修改指針指向的數據。
- 數組操作:使用Unsafe類的內存分配方法可以實現超大數組,但注意要自己釋放內存。
- 多線程同步:CAS操作、鎖機制,LockSupport的pack、unpark方法最終都是調用了Unsafe的pack、unpack方法。
- 內存屏障:在Java8中新引入的,用於定義內存屏障,避免代碼重排。
3、Java9中Unsafe的變化
- Unsafe包路徑發生變化,被移動到jdk.internal.misc包下。
- 增加了非常詳細的注釋,增加一個靜態方法,可以直接拿到theUnsafe對象。
