Java基礎(chǔ)之Unsafe內(nèi)存操作不安全類詳解
Unsafe類使Java擁有了像C語言的指針一樣操作內(nèi)存空間的能力,直接操作內(nèi)存就意味著
1、不受jvm管理,也就意味著無法被GC,需要我們手動(dòng)GC,稍有不慎就會(huì)出現(xiàn)內(nèi)存泄漏。
2、Unsafe的不少方法中必須提供原始地址(內(nèi)存地址)和被替換對(duì)象的地址,偏移量要自己計(jì)算,一旦出現(xiàn)問題就是JVM崩潰級(jí)別的異常,會(huì)導(dǎo)致整個(gè)JVM實(shí)例崩潰,表現(xiàn)為應(yīng)用程序直接crash掉。
3、直接操作內(nèi)存,也意味著其速度更快,在高并發(fā)的條件之下能夠很好地提高效率。
Unsafe 類
public final class Unsafe
Unsafe類是'final'的,不允許繼承。
Unsafe 屬性
private static final Unsafe theUnsafe;public static final int INVALID_FIELD_OFFSET = -1;public static final int ARRAY_BOOLEAN_BASE_OFFSET;public static final int ARRAY_BYTE_BASE_OFFSET;public static final int ARRAY_SHORT_BASE_OFFSET;public static final int ARRAY_CHAR_BASE_OFFSET;public static final int ARRAY_INT_BASE_OFFSET;public static final int ARRAY_LONG_BASE_OFFSET;public static final int ARRAY_FLOAT_BASE_OFFSET;public static final int ARRAY_DOUBLE_BASE_OFFSET;public static final int ARRAY_OBJECT_BASE_OFFSET;public static final int ARRAY_BOOLEAN_INDEX_SCALE;public static final int ARRAY_BYTE_INDEX_SCALE;public static final int ARRAY_SHORT_INDEX_SCALE;public static final int ARRAY_CHAR_INDEX_SCALE;public static final int ARRAY_INT_INDEX_SCALE;public static final int ARRAY_LONG_INDEX_SCALE;public static final int ARRAY_FLOAT_INDEX_SCALE;public static final int ARRAY_DOUBLE_INDEX_SCALE;public static final int ARRAY_OBJECT_INDEX_SCALE;public static final int ADDRESS_SIZE;
這些屬性都是在類加載時(shí)初始化,它們都是一些類型數(shù)組指針。
Unsafe 靜態(tài)加載
static {registerNatives();Reflection.registerMethodsToFilter(Unsafe.class, new String[]{'getUnsafe'});theUnsafe = new Unsafe();ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset(boolean[].class);ARRAY_BYTE_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);ARRAY_SHORT_BASE_OFFSET = theUnsafe.arrayBaseOffset(short[].class);ARRAY_CHAR_BASE_OFFSET = theUnsafe.arrayBaseOffset(char[].class);ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset(int[].class);ARRAY_LONG_BASE_OFFSET = theUnsafe.arrayBaseOffset(long[].class);ARRAY_FLOAT_BASE_OFFSET = theUnsafe.arrayBaseOffset(float[].class);ARRAY_DOUBLE_BASE_OFFSET = theUnsafe.arrayBaseOffset(double[].class);ARRAY_OBJECT_BASE_OFFSET = theUnsafe.arrayBaseOffset(Object[].class);ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale(boolean[].class);ARRAY_BYTE_INDEX_SCALE = theUnsafe.arrayIndexScale(byte[].class);ARRAY_SHORT_INDEX_SCALE = theUnsafe.arrayIndexScale(short[].class);ARRAY_CHAR_INDEX_SCALE = theUnsafe.arrayIndexScale(char[].class);ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale(int[].class);ARRAY_LONG_INDEX_SCALE = theUnsafe.arrayIndexScale(long[].class);ARRAY_FLOAT_INDEX_SCALE = theUnsafe.arrayIndexScale(float[].class);ARRAY_DOUBLE_INDEX_SCALE = theUnsafe.arrayIndexScale(double[].class);ARRAY_OBJECT_INDEX_SCALE = theUnsafe.arrayIndexScale(Object[].class);ADDRESS_SIZE = theUnsafe.addressSize();}private static native void registerNatives();
Unsafe 構(gòu)造函數(shù)
private Unsafe() {}
Unsafe 對(duì)象不能直接通過 new Unsafe(),它的構(gòu)造函數(shù)是私有的。
Unsafe 實(shí)例化方法
public static Unsafe getUnsafe() {Class var0 = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(var0.getClassLoader())) {throw new SecurityException('Unsafe');} else {return theUnsafe;}}
getUnsafe 只能從引導(dǎo)類加載器(bootstrap class loader)加載,非啟動(dòng)類加載器直接調(diào)用 Unsafe.getUnsafe() 方法會(huì)拋出 SecurityException 異常。解決辦法:
1、可以令代碼 ' 受信任 '。運(yùn)行程序時(shí),通過 JVM 參數(shù)設(shè)置 bootclasspath 選項(xiàng),指定系統(tǒng)類路徑加上使用的一個(gè) Unsafe 路徑。
java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.Test
2、通過 Java 反射機(jī)制,暴力獲取。
Field field = Unsafe.class.getDeclaredField('theUnsafe');field.setAccessible(true);Unsafe unsafe = (Unsafe) field.get(null);
Unsafe 內(nèi)存管理
// 獲取本地指針的大小(單位是byte),通常值為4或者8。常量ADDRESS_SIZE就是調(diào)用此方法。public native int addressSize();// 獲取本地內(nèi)存的頁數(shù),此值為2的冪次方。public native int pageSize();// 分配一塊新的本地內(nèi)存,通過bytes指定內(nèi)存塊的大小(單位是byte),返回新開辟的內(nèi)存的地址。public native long allocateMemory(long var1);// 通過指定的內(nèi)存地址address重新調(diào)整本地內(nèi)存塊的大小,調(diào)整后的內(nèi)存塊大小通過bytes指定(單位為byte)。public native long reallocateMemory(long var1, long var3);// 將給定內(nèi)存塊中的所有字節(jié)設(shè)置為固定值(通常是0)public native void setMemory(Object var1, long var2, long var4, byte var6);// 內(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);
注意:allocateMemory方法申請(qǐng)的內(nèi)存,將直接脫離jvm,gc將無法管理該方式申請(qǐng)的內(nèi)存,用完一定要手動(dòng)釋放內(nèi)存,防止內(nèi)存溢出;JDK中示例:ByteBuffer.allocateDirect(int capacity)使用DirectByteBuffer,DirectByteBuffer中就是用allocateMemory申請(qǐng)堆外內(nèi)存。
Unsafe 獲取偏移量
// 返回指定變量所屬類中的內(nèi)存偏移量public native long objectFieldOffset(Field var1);// 獲取數(shù)組中第一個(gè)元素的地址public native int arrayBaseOffset(Class<?> var1);// 獲取靜態(tài)變量地址偏移值public native long staticFieldOffset(Field var1);// 其實(shí)就是數(shù)據(jù)中元素偏移地址的增量,數(shù)組中的元素的地址是連續(xù)的public native int arrayIndexScale(Class<?> var1);
Unsafe 檢查類初始化
// 檢測(cè)給定的類是否需要初始化。// 當(dāng)ensureClassInitialized方法不生效的時(shí)候才返回falsepublic native boolean shouldBeInitialized(Class<?> c);// 檢測(cè)給定的類是否已經(jīng)初始化。public native void ensureClassInitialized(Class<?> c);
Unsafe 從指定位置讀取
// 從指定內(nèi)存地址處開始讀取一個(gè)bytepublic native byte getByte(long var1);// 從指定內(nèi)存地址處開始讀取一個(gè)short public native short getShort(long var1);// 從指定內(nèi)存地址處開始讀取一個(gè)char public native char getChar(long var1);// 從指定內(nèi)存地址處開始讀取一個(gè)int public native int getInt(long var1);// 從指定內(nèi)存地址處開始讀取一個(gè)long public native long getLong(long var1);// 從指定內(nèi)存地址處開始讀取一個(gè)float public native float getFloat(long var1);// 從指定內(nèi)存地址處開始讀取一個(gè)double public native double getDouble(long var1);
Unsafe 向指定位置寫值
// 向指定位置寫入一個(gè)int public native void putInt(long var1, int var3);// 向指定位置寫入一個(gè)char public native void putChar(long var1, char var3);// 向指定位置寫入一個(gè)byte public native void putByte(long var1, byte var3);// 向指定位置寫入一個(gè)short public native void putShort(long var1, short var3);// 向指定位置寫入一個(gè)long public native void putLong(long var1, long var3);// 向指定位置寫入一個(gè)float public native void putFloat(long var1, float var3);// 向指定位置寫入一個(gè)double public native void putDouble(long var1, double var3);
Unsafe 對(duì)象操作
從指定偏移量處讀取對(duì)象屬性(非主存)
public native int getInt(Object var1, long var2);public native Object getObject(Object var1, long var2);public native boolean getBoolean(Object var1, long var2);public native byte getByte(Object var1, long var2);public native short getShort(Object var1, long var2);public native char getChar(Object var1, long var2);public native long getLong(Object var1, long var2);public native float getFloat(Object var1, long var2);public native double getDouble(Object var1, long var2);
向指定偏移量處修改對(duì)象屬性(非主存)
public native void putInt(Object var1, long var2, int var4);public native void putObject(Object var1, long var2, Object var4);public native void putBoolean(Object var1, long var2, boolean var4);public native void putByte(Object var1, long var2, byte var4);public native void putShort(Object var1, long var2, short var4);public native void putChar(Object var1, long var2, char var4);public native void putLong(Object var1, long var2, long var4);public native void putFloat(Object var1, long var2, float var4);public native void putDouble(Object var1, long var2, double var4);
向指定偏移量處修改對(duì)象屬性(主存)
public native Object getObjectVolatile(Object var1, long var2);public native int getIntVolatile(Object var1, long var2);public native boolean getBooleanVolatile(Object var1, long var2);public native byte getByteVolatile(Object var1, long var2);public native short getShortVolatile(Object var1, long var2);public native char getCharVolatile(Object var1, long var2);public native long getLongVolatile(Object var1, long var2);public native float getFloatVolatile(Object var1, long var2);public native double getDoubleVolatile(Object var1, long var2);
向指定偏移量處修改對(duì)象屬性(主存)
public native void putObjectVolatile(Object var1, long var2, Object var4);public native void putIntVolatile(Object var1, long var2, int var4);public native void putBooleanVolatile(Object var1, long var2, boolean var4);public native void putByteVolatile(Object var1, long var2, byte var4);public native void putShortVolatile(Object var1, long var2, short var4);public native void putCharVolatile(Object var1, long var2, char var4);public native void putLongVolatile(Object var1, long var2, long var4);public native void putFloatVolatile(Object var1, long var2, float var4);public native void putDoubleVolatile(Object var1, long var2, double var4);public native void putOrderedObject(Object var1, long var2, Object var4);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);
Unsafe 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);
針對(duì)對(duì)象進(jìn)行CAS操作,本質(zhì)更新對(duì)象中指定偏移量的屬性,當(dāng)原值為var4時(shí)才會(huì)更新成var5并返回true,否則返回false。舉例:volatile i=0;有多個(gè)線程修改i的值,A線程只有在i=1時(shí)修改為2,如果代碼如下
if (i == 1) {i = 2;}
這樣時(shí)有問題的,if比較完i可能已經(jīng)被別人修改了,這種場(chǎng)景特別適合CAS,使用CAS代碼如下
boolean isUpdate = compareAndSwapInt(object, offset, 1, 2)
相當(dāng)于讀->判斷->寫一次搞定(實(shí)在不能理解CAS,可以這么理解)
Unsafe 線程的掛起和恢復(fù)
public native void park(boolean var1, long var2);
阻塞當(dāng)前線程直到一個(gè)unpark方法出現(xiàn)(被調(diào)用)、一個(gè)用于unpark方法已經(jīng)出現(xiàn)過(在此park方法調(diào)用之前已經(jīng)調(diào)用過)、線程被中斷或者time時(shí)間到期(也就是阻塞超時(shí))。在time非零的情況下,如果isAbsolute為true,time是相對(duì)于新紀(jì)元之后的毫秒,否則time表示納秒。這個(gè)方法執(zhí)行時(shí)也可能不合理地返回(沒有具體原因)。并發(fā)包java.util.concurrent中的框架對(duì)線程的掛起操作被封裝在LockSupport類中,LockSupport類中有各種版本pack方法,但最終都調(diào)用了Unsafe#park()方法。
public native void unpark(Object var1);
釋放被park創(chuàng)建的在一個(gè)線程上的阻塞。這個(gè)方法也可以被使用來終止一個(gè)先前調(diào)用park導(dǎo)致的阻塞。這個(gè)操作是不安全的,因此必須保證線程是存活的(thread has not been destroyed)。從Java代碼中判斷一個(gè)線程是否存活的是顯而易見的,但是從native代碼中這機(jī)會(huì)是不可能自動(dòng)完成的。
Unsafe 內(nèi)存屏障
public native void loadFence();
在該方法之前的所有讀操作,一定在load屏障之前執(zhí)行完成。
public native void storeFence();
在該方法之前的所有寫操作,一定在store屏障之前執(zhí)行完成
public native void fullFence();
在該方法之前的所有讀寫操作,一定在full屏障之前執(zhí)行完成,這個(gè)內(nèi)存屏障相當(dāng)于上面兩個(gè)(load屏障和store屏障)的合體功能。
Unsafe 其他
public native int getLoadAverage(double[] loadavg, int nelems);
獲取系統(tǒng)的平均負(fù)載值,loadavg這個(gè)double數(shù)組將會(huì)存放負(fù)載值的結(jié)果,nelems決定樣本數(shù)量,nelems只能取值為1到3,分別代表最近1、5、15分鐘內(nèi)系統(tǒng)的平均負(fù)載。如果無法獲取系統(tǒng)的負(fù)載,此方法返回-1,否則返回獲取到的樣本數(shù)量(loadavg中有效的元素個(gè)數(shù))。實(shí)驗(yàn)中這個(gè)方法一直返回-1,其實(shí)完全可以使用JMX中的相關(guān)方法替代此方法。
public native void throwException(Throwable ee);
繞過檢測(cè)機(jī)制直接拋出異常。
到此這篇關(guān)于Java基礎(chǔ)之Unsafe內(nèi)存操作不安全類詳解的文章就介紹到這了,更多相關(guān)Unsafe內(nèi)存操作不安全類內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. 怎樣才能用js生成xmldom對(duì)象,并且在firefox中也實(shí)現(xiàn)xml數(shù)據(jù)島?2. XHTML 1.0:標(biāo)記新的開端3. CSS3中Transition屬性詳解以及示例分享4. 低版本IE正常運(yùn)行HTML5+CSS3網(wǎng)站的3種解決方案5. ASP腳本組件實(shí)現(xiàn)服務(wù)器重啟6. css進(jìn)階學(xué)習(xí) 選擇符7. CSS Hack大全-教你如何區(qū)分出IE6-IE10、FireFox、Chrome、Opera8. ASP刪除img標(biāo)簽的style屬性只保留src的正則函數(shù)9. 告別AJAX實(shí)現(xiàn)無刷新提交表單10. ASP動(dòng)態(tài)網(wǎng)頁制作技術(shù)經(jīng)驗(yàn)分享
