簡(jiǎn)單談一談Java中的Unsafe類
Unsafe類是啥?
Java最初被設(shè)計(jì)為一種安全的受控環(huán)境。盡管如此筋栋,Java HotSpot還是包含了一個(gè)“后門(mén)”敏簿,提供了一些可以直接操控內(nèi)存和線程的低層次操作净薛。這個(gè)后門(mén)類——sun.misc.Unsafe——被JDK廣泛用于自己的包中擂送,如java.nio和java.util.concurrent弄贿。但是絲毫不建議在生產(chǎn)環(huán)境中使用這個(gè)后門(mén)液斜。因?yàn)檫@個(gè)API十分不安全求类、不輕便奔垦、而且不穩(wěn)定。這個(gè)不安全的類提供了一個(gè)觀察HotSpot JVM內(nèi)部結(jié)構(gòu)并且可以對(duì)其進(jìn)行修改仑嗅。有時(shí)它可以被用來(lái)在不適用C++調(diào)試的情況下學(xué)習(xí)虛擬機(jī)內(nèi)部結(jié)構(gòu)宴倍,有時(shí)也可以被拿來(lái)做性能監(jiān)控和開(kāi)發(fā)工具。
引言
Java當(dāng)中神奇的Unsafe類仓技,(可以稱作魔術(shù)鸵贬,哈哈)在這里跟大家分享一下。
Unsafe類是在sun.misc包下脖捻,不屬于Java標(biāo)準(zhǔn)阔逼。但是很多Java的基礎(chǔ)類庫(kù),包括一些被廣泛使用的高性能開(kāi)發(fā)庫(kù)都是基于Unsafe類開(kāi)發(fā)的地沮,比如Netty嗜浮、Cassandra、Hadoop摩疑、Kafka等危融。Unsafe類在提升Java運(yùn)行效率,增強(qiáng)Java語(yǔ)言底層操作能力方面起了很大的作用雷袋。
Unsafe類使Java擁有了像C語(yǔ)言的指針一樣操作內(nèi)存空間的能力吉殃,同時(shí)也帶來(lái)了指針的問(wèn)題。過(guò)度的使用Unsafe類會(huì)使得出錯(cuò)的幾率變大楷怒,因此Java官方并不建議使用的蛋勺,官方文檔也幾乎沒(méi)有。Oracle正在計(jì)劃從Java 9中去掉Unsafe類鸠删,如果真是如此影響就太大了抱完。
通常我們最好也不要使用Unsafe類,除非有明確的目的刃泡,并且也要對(duì)它有深入的了解才行巧娱。要想使用Unsafe類需要用一些比較tricky的辦法。Unsafe類使用了單例模式捅僵,需要通過(guò)一個(gè)靜態(tài)方法getUnsafe()來(lái)獲取家卖。但Unsafe類做了限制,如果是普通的調(diào)用的話庙楚,它會(huì)拋出一個(gè)SecurityException異常;只有由主類加載器加載的類才能調(diào)用這個(gè)方法趴樱。其源碼如下:
1
2
3
4
5
6
7
8
publicstaticUnsafe getUnsafe() {
?Class var0 = Reflection.getCallerClass();
?if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
??thrownewSecurityException("Unsafe");
?} else{
??returntheUnsafe;
?}
}
網(wǎng)上也有一些辦法來(lái)用主類加載器加載用戶代碼馒闷,比如設(shè)置bootclasspath參數(shù)酪捡。但更簡(jiǎn)單方法是利用Java反射,方法如下:
1
2
3
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
獲取到Unsafe實(shí)例之后纳账,我們就可以為所欲為了逛薇。Unsafe類提供了以下這些功能:
一、內(nèi)存管理疏虫。包括分配內(nèi)存永罚、釋放內(nèi)存等。
該部分包括了allocateMemory(分配內(nèi)存)卧秘、reallocateMemory(重新分配內(nèi)存)呢袱、copyMemory(拷貝內(nèi)存)、freeMemory(釋放內(nèi)存 )翅敌、getAddress(獲取內(nèi)存地址)羞福、addressSize、pageSize蚯涮、getInt(獲取內(nèi)存地址指向的整數(shù))治专、getIntVolatile(獲取內(nèi)存地址指向的整數(shù),并支持volatile語(yǔ)義)遭顶、putInt(將整數(shù)寫(xiě)入指定內(nèi)存地址)张峰、putIntVolatile(將整數(shù)寫(xiě)入指定內(nèi)存地址,并支持volatile語(yǔ)義)棒旗、putOrderedInt(將整數(shù)寫(xiě)入指定內(nèi)存地址喘批、有序或者有延遲的方法)等方法。getXXX和putXXX包含了各種基本類型的操作嗦哆。
利用copyMemory方法谤祖,我們可以實(shí)現(xiàn)一個(gè)通用的對(duì)象拷貝方法,無(wú)需再對(duì)每一個(gè)對(duì)象都實(shí)現(xiàn)clone方法老速,當(dāng)然這通用的方法只能做到對(duì)象淺拷貝粥喜。
二、非常規(guī)的對(duì)象實(shí)例化橘券。
allocateInstance()方法提供了另一種創(chuàng)建實(shí)例的途徑额湘。通常我們可以用new或者反射來(lái)實(shí)例化對(duì)象,使用allocateInstance()方法可以直接生成對(duì)象實(shí)例旁舰,且無(wú)需調(diào)用構(gòu)造方法和其它初始化方法锋华。
這在對(duì)象反序列化的時(shí)候會(huì)很有用,能夠重建和設(shè)置final字段箭窜,而不需要調(diào)用構(gòu)造方法毯焕。
三、操作類、對(duì)象纳猫、變量婆咸。
這部分包括了staticFieldOffset(靜態(tài)域偏移)、defineClass(定義類)芜辕、defineAnonymousClass(定義匿名類)尚骄、ensureClassInitialized(確保類初始化)、objectFieldOffset(對(duì)象域偏移)等方法侵续。
通過(guò)這些方法我們可以獲取對(duì)象的指針倔丈,通過(guò)對(duì)指針進(jìn)行偏移,我們不僅可以直接修改指針指向的數(shù)據(jù)(即使它們是私有的)状蜗,甚至可以找到JVM已經(jīng)認(rèn)定為垃圾需五、可以進(jìn)行回收的對(duì)象。
四诗舰、數(shù)組操作警儒。
這部分包括了arrayBaseOffset(獲取數(shù)組第一個(gè)元素的偏移地址)、arrayIndexScale(獲取數(shù)組中元素的增量地址)等方法眶根。arrayBaseOffset與arrayIndexScale配合起來(lái)使用蜀铲,就可以定位數(shù)組中每個(gè)元素在內(nèi)存中的位置。
由于Java的數(shù)組最大值為Integer.MAX_VALUE属百,使用Unsafe類的內(nèi)存分配方法可以實(shí)現(xiàn)超大數(shù)組记劝。實(shí)際上這樣的數(shù)據(jù)就可以認(rèn)為是C數(shù)組,因此需要注意在合適的時(shí)間釋放內(nèi)存族扰。
五厌丑、多線程同步。包括鎖機(jī)制渔呵、CAS操作等怒竿。
這部分包括了monitorEnter、tryMonitorEnter扩氢、monitorExit耕驰、compareAndSwapInt、compareAndSwap等方法录豺。
其中monitorEnter朦肘、tryMonitorEnter、monitorExit已經(jīng)被標(biāo)記為deprecated双饥,不建議使用媒抠。
Unsafe類的CAS操作可能是用的最多的,它為Java的鎖機(jī)制提供了一種新的解決辦法咏花,比如AtomicInteger等類都是通過(guò)該方法來(lái)實(shí)現(xiàn)的趴生。compareAndSwap方法是原子的,可以避免繁重的鎖機(jī)制,提高代碼效率冲秽。這是一種樂(lè)觀鎖舍咖,通常認(rèn)為在大部分情況下不出現(xiàn)競(jìng)態(tài)條件矩父,如果操作失敗锉桑,會(huì)不斷重試直到成功。
六窍株、掛起與恢復(fù)民轴。
這部分包括了park、unpark等方法球订。
將一個(gè)線程進(jìn)行掛起是通過(guò)park方法實(shí)現(xiàn)的后裸,調(diào)用 park后,線程將一直阻塞直到超時(shí)或者中斷等條件出現(xiàn)冒滩。unpark可以終止一個(gè)掛起的線程微驶,使其恢復(fù)正常。整個(gè)并發(fā)框架中對(duì)線程的掛起操作被封裝在 LockSupport類中开睡,LockSupport類中有各種版本pack方法因苹,但最終都調(diào)用了Unsafe.park()方法。
七篇恒、內(nèi)存屏障扶檐。
這部分包括了loadFence、storeFence胁艰、fullFence等方法款筑。這是在Java 8新引入的,用于定義內(nèi)存屏障腾么,避免代碼重排序奈梳。
loadFence() 表示該方法之前的所有l(wèi)oad操作在內(nèi)存屏障之前完成。同理storeFence()表示該方法之前的所有store操作在內(nèi)存屏障之前完成解虱。fullFence()表示該方法之前的所有l(wèi)oad攘须、store操作在內(nèi)存屏障之前完成。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了饭寺,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值阻课,如果有疑問(wèn)大家可以留言交流。