Java和C++語(yǔ)言的一個(gè)重要區(qū)別就是Java中我們無(wú)法直接操作一塊內(nèi)存區(qū)域,不能像C++中那樣可以自己申請(qǐng)內(nèi)存和釋放內(nèi)存乎完。Java中的Unsafe類為我們提供了類似C++手動(dòng)管理內(nèi)存的能力瞒瘸。
Unsafe類宏所,全限定名是sun.misc.Unsafe
冗懦,從名字中我們可以看出來(lái)這個(gè)類對(duì)普通程序員來(lái)說(shuō)是“危險(xiǎn)”的景殷,一般應(yīng)用開(kāi)發(fā)者不會(huì)用到這個(gè)類离福。
Unsafe類是"final"的杖狼,不允許繼承。且構(gòu)造函數(shù)是private的:
public final class Unsafe {
private static final Unsafe theUnsafe;
public static final int INVALID_FIELD_OFFSET = -1;
private static native void registerNatives();
// 構(gòu)造函數(shù)是private的妖爷,不允許外部實(shí)例化
private Unsafe() {
}
...
}
因此我們無(wú)法在外部對(duì)Unsafe進(jìn)行實(shí)例化蝶涩。
獲取Unsafe
Unsafe無(wú)法實(shí)例化,那么怎么獲取Unsafe呢赠涮?答案就是通過(guò)反射來(lái)獲取Unsafe:
public Unsafe getUnsafe() throws IllegalAccessException {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
return unsafe;
}
主要功能
Unsafe的功能如下圖:
普通讀寫
通過(guò)Unsafe可以讀寫一個(gè)類的屬性子寓,即使這個(gè)屬性是私有的,也可以對(duì)這個(gè)屬性進(jìn)行讀寫笋除。
讀寫一個(gè)Object屬性的相關(guān)方法
public native int getInt(Object var1, long var2);
public native void putInt(Object var1, long var2, int var4);
getInt用于從對(duì)象的指定偏移地址處讀取一個(gè)int斜友。putInt用于在對(duì)象指定偏移地址處寫入一個(gè)int。其他的primitive type也有對(duì)應(yīng)的方法垃它。
Unsafe還可以直接在一個(gè)地址上讀寫
public native byte getByte(long var1);
public native void putByte(long var1, byte var3);
getByte用于從指定內(nèi)存地址處開(kāi)始讀取一個(gè)byte鲜屏。putByte用于從指定內(nèi)存地址寫入一個(gè)byte。其他的primitive type也有對(duì)應(yīng)的方法国拇。
volatile讀寫
普通的讀寫無(wú)法保證可見(jiàn)性和有序性洛史,而volatile讀寫就可以保證可見(jiàn)性和有序性。
public native int getIntVolatile(Object var1, long var2);
public native void putIntVolatile(Object var1, long var2, int var4);
getIntVolatile方法用于在對(duì)象指定偏移地址處volatile讀取一個(gè)int酱吝。putIntVolatile方法用于在對(duì)象指定偏移地址處volatile寫入一個(gè)int也殖。
volatile讀寫相對(duì)普通讀寫是更加昂貴的,因?yàn)樾枰WC可見(jiàn)性和有序性,而與volatile寫入相比putOrderedXX寫入代價(jià)相對(duì)較低忆嗜,putOrderedXX寫入不保證可見(jiàn)性己儒,但是保證有序性,所謂有序性捆毫,就是保證指令不會(huì)重排序闪湾。
有序?qū)懭?/h2>
有序?qū)懭胫槐WC寫入的有序性,不保證可見(jiàn)性绩卤,就是說(shuō)一個(gè)線程的寫入不保證其他線程立馬可見(jiàn)途样。
public native void putOrderedObject(Object var1, long var2, Object var4);
public native void putOrderedInt(Object var1, long var2, int var4);
public native void putOrderedLong(Object var1, long var2, long var4);
直接內(nèi)存操作
我們都知道Java不可以直接對(duì)內(nèi)存進(jìn)行操作,對(duì)象內(nèi)存的分配和回收都是由JVM幫助我們實(shí)現(xiàn)的濒憋。但是Unsafe為我們?cè)贘ava中提供了直接操作內(nèi)存的能力何暇。
// 分配內(nèi)存
public native long allocateMemory(long var1);
// 重新分配內(nèi)存
public native long reallocateMemory(long var1, long var3);
// 內(nèi)存初始化
public native void setMemory(long var1, long var3, byte var5);
// 內(nèi)存復(fù)制
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
// 清除內(nèi)存
public native void freeMemory(long var1);
CAS相關(guān)
JUC中大量運(yùn)用了CAS操作,可以說(shuō)CAS操作是JUC的基礎(chǔ)跋炕,因此CAS操作是非常重要的赖晶。Unsafe中提供了int,long和Object的CAS操作:
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一般用于樂(lè)觀鎖律适,它在Java中有廣泛的應(yīng)用辐烂,ConcurrentHashMap,ConcurrentLinkedQueue中都有用到CAS來(lái)實(shí)現(xiàn)樂(lè)觀鎖捂贿。
偏移量相關(guān)
public native long staticFieldOffset(Field var1);
public native long objectFieldOffset(Field var1);
public native Object staticFieldBase(Field var1);
public native int arrayBaseOffset(Class<?> var1);
public native int arrayIndexScale(Class<?> var1);
staticFieldOffset方法用于獲取靜態(tài)屬性Field在對(duì)象中的偏移量纠修,讀寫靜態(tài)屬性時(shí)必須獲取其偏移量。objectFieldOffset方法用于獲取非靜態(tài)屬性Field在對(duì)象實(shí)例中的偏移量厂僧,讀寫對(duì)象的非靜態(tài)屬性時(shí)會(huì)用到這個(gè)偏移量扣草。staticFieldBase方法用于返回Field所在的對(duì)象。arrayBaseOffset方法用于返回?cái)?shù)組中第一個(gè)元素實(shí)際地址相對(duì)整個(gè)數(shù)組對(duì)象的地址的偏移量颜屠。arrayIndexScale方法用于計(jì)算數(shù)組中第一個(gè)元素所占用的內(nèi)存空間辰妙。
線程調(diào)度
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方法相信看過(guò)LockSupport類的都不會(huì)陌生,這兩個(gè)方法主要用來(lái)掛起和喚醒線程甫窟。LockSupport中的park和unpark方法正是通過(guò)Unsafe來(lái)實(shí)現(xiàn)的:
// 掛起線程
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker); // 通過(guò)Unsafe的putObject方法設(shè)置阻塞阻塞當(dāng)前線程的blocker
UNSAFE.park(false, 0L); // 通過(guò)Unsafe的park方法來(lái)阻塞當(dāng)前線程密浑,注意此方法將當(dāng)前線程阻塞后,當(dāng)前線程就不會(huì)繼續(xù)往下走了粗井,直到其他線程unpark此線程
setBlocker(t, null); // 清除blocker
}
// 喚醒線程
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
monitorEnter方法和monitorExit方法用于加鎖尔破,Java中的synchronized鎖就是通過(guò)這兩個(gè)指令來(lái)實(shí)現(xiàn)的。
類加載
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 Object allocateInstance(Class<?> var1) throws InstantiationException;
public native boolean shouldBeInitialized(Class<?> var1);
public native void ensureClassInitialized(Class<?> var1);
defineClass方法定義一個(gè)類浇衬,用于動(dòng)態(tài)地創(chuàng)建類懒构。
defineAnonymousClass用于動(dòng)態(tài)的創(chuàng)建一個(gè)匿名內(nèi)部類。
allocateInstance方法用于創(chuàng)建一個(gè)類的實(shí)例耘擂,但是不會(huì)調(diào)用這個(gè)實(shí)例的構(gòu)造方法胆剧,如果這個(gè)類還未被初始化,則初始化這個(gè)類醉冤。
shouldBeInitialized方法用于判斷是否需要初始化一個(gè)類秩霍。
ensureClassInitialized方法用于保證已經(jīng)初始化過(guò)一個(gè)類滚朵。
內(nèi)存屏障
public native void loadFence();
public native void storeFence();
public native void fullFence();
loadFence:保證在這個(gè)屏障之前的所有讀操作都已經(jīng)完成。
storeFence:保證在這個(gè)屏障之前的所有寫操作都已經(jīng)完成前域。
fullFence:保證在這個(gè)屏障之前的所有讀寫操作都已經(jīng)完成辕近。