1.JAVA提供的操作記憶體類
sun.misc.Unsafe 操作類
複製程式碼
此類在提升Java執行效率,很多Java基礎類庫都使用了此類以提升效能。
此類使Java擁有了像C語言的指標一樣操作記憶體空間的能力,但是也使程式易錯。
2.程式碼片段
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if(var0.getClassLoader() != null) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
複製程式碼
getUnsafe()方法只能由ClassLoader == null的類呼叫,意味著呼叫這個方法的類是jdk中C程式碼載入的。
3.可以使用的方式
public static Unsafe getUnsafe() {
try {
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
// the unsafe instance
return (Unsafe) unsafeField.get(null);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
複製程式碼
使用反射可以獲取到Unsafe操作類例項。
4.Unsafe基本操作
- 操作成員變數
public class Dog {
private String name;
private int age;
private long size;
}
複製程式碼
public static void main(String[] args) throws Exception{
Unsafe unsafe = getUnsafe();
Dog dog = new Dog();
// 獲取name屬性偏移量
long nameOffset = unsafe.objectFieldOffset(Dog.class.getDeclaredField("name"));
unsafe.putObject(dog, nameOffset, "Rick");
// 獲取age屬性偏移量
long ageOffset = unsafe.objectFieldOffset(Dog.class.getDeclaredField("age"));
unsafe.putInt(dog, ageOffset, 11);
// 獲取size屬性偏移量
long sizeOffset = unsafe.objectFieldOffset(Dog.class.getDeclaredField("size"));
unsafe.putLong(dog, sizeOffset, 1000L);
System.out.println(dog);
}
複製程式碼
- 運算元組
@Test
public void testArray(){
Unsafe unsafe = UnsafeMain.getUnsafe();
// pigs[]陣列基本偏移量
int base = unsafe.arrayBaseOffset(Pig[].class);
// pigs[]陣列的單位偏移量
int indexScale = unsafe.arrayIndexScale(Pig[].class);
// unsafe運算元組
Pig[] pigs = new Pig[8];
Pig pig = new Pig("Rick", 10, 800L);
unsafe.compareAndSwapObject(pigs, base + 5 * indexScale, null, pig);
System.out.println(pigs[5]);
}
複製程式碼
- 記憶體操作
@Test
public void testMemory(){
Unsafe unsafe = UnsafeMain.getUnsafe();
// 申請記憶體
long index = unsafe.allocateMemory(1L);
// 設定記憶體
unsafe.putByte(index, (byte)-127);
// 根據地址獲取資料
System.out.println(unsafe.getByte(index));
// 釋放記憶體
unsafe.freeMemory(index);
}
複製程式碼
- 多執行緒同步(包括鎖機制,CAS操作等)
這部分包括了monitorEnter、tryMonitorEnter、monitorExit、compareAndSwapInt、compareAndSwap等方法。
其中monitorEnter、tryMonitorEnter、monitorExit已經被標記為deprecated,不建議使用。
Unsafe類的CAS操作可能是用的最多的,它為Java的鎖機制提供了一種新的解決辦法,比如AtomicInteger等類都是通過該方法來實現的。compareAndSwap方法是原子的,可以避免繁重的鎖機制,提高程式碼效率。這是一種樂觀鎖,通常認為在大部分情況下不出現競態條件,如果操作失敗,會不斷重試直到成功。
- 掛起與恢復
這部分包括了park、unpark等方法。
將一個執行緒進行掛起是通過park方法實現的,呼叫 park後,執行緒將一直阻塞直到超時或者中斷等條件出現。unpark可以終止一個掛起的執行緒,使其恢復正常。整個併發框架中對執行緒的掛起操作被封裝在 LockSupport類中,LockSupport類中有各種版本pack方法,但最終都呼叫了Unsafe.park()方法。
- 記憶體屏障
這部分包括了loadFence、storeFence、fullFence等方法。這是在Java 8新引入的,用於定義記憶體屏障,避免程式碼重排序。
loadFence() 表示該方法之前的所有load操作在記憶體屏障之前完成。同理storeFence()表示該方法之前的所有store操作在記憶體屏障之前完成。fullFence()表示該方法之前的所有load、store操作在記憶體屏障之前完成。
5.總結
一些JDK原始碼中使用了此Unsafe,如ConcurrentHashMap等。為之後的分析做準備。