本篇內(nèi)容介紹了“有哪些Unsafe類”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)建站專注于企業(yè)全網(wǎng)營(yíng)銷推廣、網(wǎng)站重做改版、曲麻萊網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、HTML5、電子商務(wù)商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)營(yíng)銷網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為曲麻萊等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。
一、簡(jiǎn)單介紹
首先在Oracle的Jdk8無(wú)法獲取到sun.misc包的源碼,想看此包的源碼可以直接下載openjdk。
1、預(yù)備工作
openjdk的源碼我下載的是openjdk-8u40-src-b25-10_feb_2015,有需要的可以私信我,如果是我公眾號(hào)粉絲,我會(huì)直接附加上這個(gè)百度云資源。在下載完成之后,然后就可以直接導(dǎo)入我們的eclipse了。
windows->preference->installed jres->選中jre->edit->rt.jar->source attachment->external folders->openjdk源碼路徑。此時(shí)就可以查看我們的Unsafe類的源碼了。
2、簡(jiǎn)介說(shuō)明
如果你學(xué)習(xí)了一些java并發(fā)包里面的類源碼的話,對(duì)這個(gè)Unsafe類一定不陌生,整個(gè)java并發(fā)包底層實(shí)現(xiàn)的核心就是它,在很久之前盛傳著這個(gè)類將要在jdk9移除,事實(shí)上如果移除了那么一大批框架將會(huì)消失,比如說(shuō)赫赫有名的Netty框架。最終jdk9出現(xiàn)的時(shí)候也只是對(duì)其進(jìn)行了改進(jìn)和優(yōu)化。不過(guò)這也再一次說(shuō)明了這個(gè)類的重要地位。
為什么說(shuō)它一半是天使一半是魔鬼呢?要回答這個(gè)問(wèn)題,我們還是要從其特性來(lái)解釋。
Unsafe類使Java擁有了像C語(yǔ)言的指針一樣操作內(nèi)存空間的能力,一旦能夠直接操作內(nèi)存,這也就意味著(1)不受jvm管理,也就意味著無(wú)法被GC,需要我們手動(dòng)GC,稍有不慎就會(huì)出現(xiàn)內(nèi)存泄漏。
(2)Unsafe的不少方法中必須提供原始地址(內(nèi)存地址)和被替換對(duì)象的地址,偏移量要自己計(jì)算,一旦出現(xiàn)問(wèn)題就是JVM崩潰級(jí)別的異常,會(huì)導(dǎo)致整個(gè)JVM實(shí)例崩潰,表現(xiàn)為應(yīng)用程序直接crash掉。
(3)直接操作內(nèi)存,也意味著其速度更快,在高并發(fā)的條件之下能夠很好地提高效率。
因此,從上面三個(gè)角度來(lái)看,雖然在一定程度上提升了效率但是也帶來(lái)了指針的不安全性。
下面我們深入到源碼中看看,提供了什么方法直接操作內(nèi)存。
二、源碼分析
Unsafe中一共有82個(gè)public native修飾的方法,還有幾十個(gè)基于這82個(gè)public native方法的其他方法。這些方法大體可以歸結(jié)為以下幾類:
(1)初始化操作
(2)操作對(duì)象屬性
(3)操作數(shù)組元素
(4)內(nèi)存管理
(5)內(nèi)存屏障
(6)線程掛起和回復(fù)
(7)CAS機(jī)制
下面我們對(duì)這些方法盡進(jìn)行一個(gè)大致的分析。
1、初始化
//1、注冊(cè)native方法,是的Unsafe類可以操作C語(yǔ)言 private static native void registerNatives(); static { registerNatives(); sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe"); } //2、構(gòu)造方法 private Unsafe() {} //3、初始化方法 private static final Unsafe theUnsafe = new Unsafe(); //4、初始化方法實(shí)現(xiàn) @CallerSensitive public static Unsafe getUnsafe() { Class> caller = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(caller.getClassLoader())) throw new SecurityException("Unsafe"); return theUnsafe; }
在這里我們看到Unsafe的初始化方法主要是通過(guò)getUnsafe方法的單例模式實(shí)現(xiàn)的,調(diào)用JVM本地方法registerNatives()和sun.reflect.Reflection,通過(guò)Reflection的getCallerClass判斷當(dāng)前調(diào)用的類是否是主類加載器(BootStrap classLoader)加載的,否則的話拋出一個(gè)SecurityException。這也證明了一個(gè)問(wèn)題,那就是只有由主類加載器(BootStrap classLoader)加載的類才能調(diào)用這個(gè)類中的方法。
2、操作屬性方法
(1)public native Object getObject(Object o, long offset);
通過(guò)給定的Java變量獲取引用值。這里實(shí)際上是獲取一個(gè)Java對(duì)象o中,獲取偏移地址為offset的屬性的值,此方法可以突破修飾符的抑制,也就是無(wú)視private、protected和default修飾符。類似的方法有g(shù)etInt、getDouble等等。同理還有putObject方法。
(2)public native Object getObjectVolatile(Object o, long offset);
強(qiáng)制從主存中獲取屬性值。類似的方法有g(shù)etIntVolatile、getDoubleVolatile等等。同理還有putObjectVolatile。
(3)public native void putOrderedObject(Object o, long offset, Object x);
設(shè)置o對(duì)象中offset偏移地址offset對(duì)應(yīng)的Object型field的值為指定值x。這是一個(gè)有序或者有延遲的putObjectVolatile方法,并且不保證值的改變被其他線程立即看到。只有在field被volatile修飾并且期望被修改的時(shí)候使用才會(huì)生效。類似的方法有putOrderedInt和putOrderedLong。
(4)public native long staticFieldOffset(Field f);
返回給定的靜態(tài)屬性在它的類的存儲(chǔ)分配中的位置(偏移地址)。
(5)public native long objectFieldOffset(Field f);
返回給定的非靜態(tài)屬性在它的類的存儲(chǔ)分配中的位置(偏移地址)。
(6)public native Object staticFieldBase(Field f);
返回給定的靜態(tài)屬性的位置,配合staticFieldOffset方法使用。
3、操作數(shù)組
(1)public native int arrayBaseOffset(Class arrayClass);
返回?cái)?shù)組類型的第一個(gè)元素的偏移地址(基礎(chǔ)偏移地址)。
(2)public native int arrayIndexScale(Class arrayClass);
返回?cái)?shù)組中元素與元素之間的偏移地址的增量。
這兩個(gè)方法配合使用就可以定位到任何一個(gè)元素的地址。
4、內(nèi)存管理
(1)public native int addressSize();
獲取本地指針的大小(單位是byte),通常值為4或者8。常量ADDRESS_SIZE就是調(diào)用此方法。
(2)public native int pageSize();
獲取本地內(nèi)存的頁(yè)數(shù),此值為2的冪次方。
(3)public native long allocateMemory(long bytes);
分配一塊新的本地內(nèi)存,通過(guò)bytes指定內(nèi)存塊的大小(單位是byte),返回新開(kāi)辟的內(nèi)存的地址。
(4)public native long reallocateMemory(long address, long bytes);
通過(guò)指定的內(nèi)存地址address重新調(diào)整本地內(nèi)存塊的大小,調(diào)整后的內(nèi)存塊大小通過(guò)bytes指定(單位為byte)。
(5)public native void setMemory(Object o, long offset, long bytes, byte value);
將給定內(nèi)存塊中的所有字節(jié)設(shè)置為固定值(通常是0)。
5、線程掛起和恢復(fù)
(1)public native void unpark(Object thread);
釋放被park創(chuàng)建的在一個(gè)線程上的阻塞。由于其不安全性,因此必須保證線程是存活的。
(2)public native void park(boolean isAbsolute, long time);`
阻塞當(dāng)前線程,一直等道unpark方法被調(diào)用。
6、內(nèi)存屏障
(1)public native void loadFence();
在該方法之前的所有讀操作,一定在load屏障之前執(zhí)行完成。
(2)public native void storeFence();
在該方法之前的所有寫(xiě)操作,一定在store屏障之前執(zhí)行完成
(3)public native void fullFence();
在該方法之前的所有讀寫(xiě)操作,一定在full屏障之前執(zhí)行完成,這個(gè)內(nèi)存屏障相當(dāng)于上面兩個(gè)(load屏障和store屏障)的合體功能。
7、CAS機(jī)制
public final native boolean compareAndSwapObject( Object o, long offset, Object expected, Object x); public final native boolean compareAndSwapInt( Object o, long offset,int expected, int x); public final native boolean compareAndSwapLong( Object o, long offset, long expected,long x);
這個(gè)Unsafe類其實(shí)是貫穿到整個(gè)java并發(fā)包體系中的,不管是你看原子包還是lock包底部都有這樣的一個(gè)類,我們需要記住的不是每一個(gè)方法,而是上面七類的標(biāo)題。也就是具體有什么功能。
三、使用
說(shuō)了這么久的源碼在這里才介紹其使用,是因?yàn)楣俜讲⒉煌扑]我們使用,也就是說(shuō)我們無(wú)法直接new出來(lái)一個(gè)Unsafe類出來(lái),那我們?cè)撊绾问褂媚?在很久之前我曾寫(xiě)過(guò)一篇介紹java反射機(jī)制的文章,沒(méi)錯(cuò)就是這個(gè)反射機(jī)制,牛的不行。Unsafe就可以通過(guò)反射機(jī)制來(lái)獲取。
public class UnsafeTest { public static void main(String[] args) throws Exception { //這里的theUnsafe就是我們?cè)创a中的那個(gè)theUnsafe Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafe.get(null); //1、創(chuàng)建對(duì)象實(shí)例 Author author = (Author) unsafe.allocateInstance(Author.class); //2、操作對(duì)象的屬性 Field ageField = Author.class.getDeclaredField("age"); long fieldOffset = unsafe.objectFieldOffset(ageField); //3、操作數(shù)組 String[] strings = new String[]{"1", "2", "3"}; long i = unsafe.arrayBaseOffset(String[].class); //4、操作內(nèi)存 long address = unsafe.allocateMemory(8L); } }
在這里只是給出一些簡(jiǎn)單的例子,其用法可以參照源碼分析中那七個(gè)方向。
注意:面試時(shí)有個(gè)小技巧,對(duì)于java語(yǔ)言特性而言,有很多違背java語(yǔ)言設(shè)計(jì)的功能都可以用這個(gè)類去回答
“有哪些Unsafe類”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!