并發(fā)作為 Java 中非常重要的一部分,其內(nèi)部大量使用了 Unsafe 類(lèi),它為 java.util.concurrent 包中的類(lèi)提供了底層支持。然而 Unsafe 并不是 JDK 的標(biāo)準(zhǔn),它是 Sun?的內(nèi)部實(shí)現(xiàn),存在于 sun.misc 包中,在 Oracle 發(fā)行的 JDK 中并不包含其源代碼。
10年積累的做網(wǎng)站、網(wǎng)站建設(shè)經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶(hù)對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶(hù)得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有平魯免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
Unsafe 提供兩個(gè)功能:
繞過(guò) JVM 直接修改內(nèi)存(對(duì)象)
使用硬件 CPU 指令實(shí)現(xiàn) CAS 原子操作
雖然我們?cè)谝话愕牟l(fā)編程中不會(huì)直接用到 Unsafe,但是很多 Java 基礎(chǔ)類(lèi)庫(kù)與諸如?Netty、Cassandra 和?Kafka 等高性能庫(kù)都采用它,它在提升 Java 運(yùn)行效率、增強(qiáng) Java 語(yǔ)言底層操作能力方面起了很大作用。筆者覺(jué)得了解一個(gè)使用如此廣泛的庫(kù)還是很有必要的。本文將深入到 Unsafe 的源碼,分析一下它的邏輯。
本文使用 OpenJDK(jdk8-b120)中 Unsafe 的源碼,Unsafe 的實(shí)現(xiàn)是和虛擬機(jī)實(shí)現(xiàn)相關(guān)的,不同的虛擬機(jī)實(shí)現(xiàn),它們的對(duì)象結(jié)構(gòu)可能不一樣,這個(gè) Unsafe 只能用于 Hotspot 虛擬機(jī)。
上源碼
Unsafe 為調(diào)用者提供執(zhí)行非安全操作的能力,由于返回的 Unsafe 對(duì)象可以讀寫(xiě)任意的內(nèi)存地址數(shù)據(jù),調(diào)用者應(yīng)該小心謹(jǐn)慎的使用改對(duì)象,一定不用把它傳遞到非信任代碼。該類(lèi)的大部分方法都是非常底層的操作,并牽涉到一小部分典型的機(jī)器都包含的硬件指令,編譯器可以對(duì)這些進(jìn)行優(yōu)化。
public?final?class?Unsafe?{????private?static?native?void?registerNatives();????static?{ ????????registerNatives(); ????????sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class,?"getUnsafe"); ????}????private?Unsafe()?{}????private?static?final?Unsafe?theUnsafe?=?new?Unsafe();????@CallerSensitive ????public?static?Unsafe?getUnsafe()?{ ????????Class<?>?caller?=?Reflection.getCallerClass();????????if?(!VM.isSystemDomainLoader(caller.getClassLoader()))????????????throw?new?SecurityException("Unsafe");????????return?theUnsafe; ????} ????...... }
上面的代碼包含如下功能:
本地靜態(tài)方法:registerNatives(),該方法會(huì)在靜態(tài)塊中執(zhí)行
私有構(gòu)造函數(shù):該類(lèi)實(shí)例是單例的,不能實(shí)例化,可以通過(guò) getUnsafe() 方法獲取實(shí)例
靜態(tài)單例方法:getUnsafe(),獲取實(shí)例
靜態(tài)塊:包含初始化的注冊(cè)功能
要使用此類(lèi)必須獲得其實(shí)例,獲得實(shí)例的方法是 getUnsafe(),那就先看看這個(gè)方法。
getUnsafe()?方法包含一個(gè)注釋 @CallerSensitive,說(shuō)明該方法不是誰(shuí)都可以調(diào)用的。如果調(diào)用者不是由系統(tǒng)類(lèi)加載器(bootstrap classloader)加載,則將拋出 SecurityException,所以默認(rèn)情況下,應(yīng)用代碼調(diào)用此方法將拋出異常。我們的代碼要想通過(guò) getUnsafe() 獲取實(shí)例是不可能的了,不過(guò)可通過(guò)反射獲取 Unsafe 實(shí)例:
Field?f=?Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); U=?(Unsafe)?f.get(null);
此處通過(guò)反射獲取類(lèi)的靜態(tài)字段,這樣就繞過(guò)了 getUnsafe() 的安全限制。
也可以通過(guò)反射獲取構(gòu)造方法再實(shí)例化,但這樣違法了該類(lèi)單例的原則,并且在使用上可能會(huì)有其它問(wèn)題,所以不建議這樣做。
?
再來(lái)看看如何獲取指定變量的值:
public?native?int?getInt(Object?o,?long?offset);
獲取指定對(duì)象中指定偏移量的字段或數(shù)組元素的值,參數(shù) o 是變量關(guān)聯(lián)的 Java 堆對(duì)象,如果指定的對(duì)象為 null,則獲取內(nèi)存中該地址的值(即 offset 為內(nèi)存絕對(duì)地址)。
如果不符合以下任意條件,則該方法返回的值是不確定的:
offset 通過(guò) objectFieldOffset 方法獲取的類(lèi)的某一字段的偏移量,并且關(guān)聯(lián)的對(duì)象 o 是該類(lèi)的兼容對(duì)象(對(duì)象 o 所在的類(lèi)必須是該類(lèi)或該類(lèi)的子類(lèi))
offset 通過(guò) staticFieldOffset 方法獲取的類(lèi)的某一字段的偏移量,o 是通過(guò) staticFieldBase 方法獲取的對(duì)象
如果 o 引用的是數(shù)組,則 offset 的值為 B+N*S,其中 N 是數(shù)組的合法下標(biāo),B 是通過(guò) arrayBaseOffset 方法從該數(shù)組類(lèi)獲取的,S 是通過(guò) arrayIndexScale 方法從該數(shù)組類(lèi)獲取的
如果以上任意條件符合,則調(diào)用者能獲取 Java 字段的引用,但是如果該字段的類(lèi)型和該方法返回的類(lèi)型不一致,則結(jié)果是不一定的,比如該字段是 short,但調(diào)用了 getInt 方法。
該方法通過(guò)兩個(gè)參數(shù)引用一個(gè)變量,它為 Java 變量提供 double-register 地址模型。如果引用的對(duì)象為 null,則該方法將 offset 當(dāng)作內(nèi)存絕對(duì)地址,就像 getInt(long)一樣,它為非 Java 變量提供 single-register 地址模型,然而 Java 變量的內(nèi)存布局可能和非 Java 變量的內(nèi)存布局不同,不應(yīng)該假設(shè)這兩種地址模型是相等的。同時(shí),應(yīng)該記住 double-register 地址模型的偏移量不應(yīng)該和 single-register 地址模型中的地址(long 參數(shù))混淆。
?
再看條件中提到的幾個(gè)相關(guān)方法:
public?native?long?objectFieldOffset(Field?f);public?native?Object?staticFieldBase(Field?f);public?native?long?staticFieldOffset(Field?f);public?native?int?arrayBaseOffset(Class?arrayClass);public?native?int?arrayIndexScale(Class?arrayClass);
這幾個(gè)方法分別是獲取靜態(tài)字段、非靜態(tài)字段與數(shù)組字段的一些信息。
objectFieldOffset
很難想象 JVM 需要使用這么多比特位來(lái)編碼非數(shù)組對(duì)象的偏移量,為了和該類(lèi)的其它方法保持一致,所以該方法也返回 long 類(lèi)型。
staticFieldBase
獲取指定靜態(tài)字段的位置,和 staticFieldOffset 一起使用。獲取該靜態(tài)字段所在的“對(duì)象”可通過(guò)類(lèi)似 getInt(Object,long)的方法訪問(wèn)。
staticFieldOffset
返回給定的字段在該類(lèi)的偏移地址。對(duì)于任何給定的字段,該方法總是返回相同的值,同一個(gè)類(lèi)的不同字段總是返回不同的值。從 1.4.1 開(kāi)始,字段的偏移以 long 表示,雖然 Sun 的 JVM 只使用了 32 位,但是那些將靜態(tài)字段存儲(chǔ)到絕對(duì)地址的 JVM 實(shí)現(xiàn)需要使用 long 類(lèi)型的偏移量,通過(guò) getXX(null,long) 獲取字段值,為了保持代碼遷移到 64 位平臺(tái)上 JVM 的優(yōu)良性,必須保持靜態(tài)字段偏移量的所有比。
arrayBaseOffset
返回給定數(shù)組類(lèi)第一個(gè)元素在內(nèi)存中的偏移量。如果 arrayIndexScale 方法返回非 0 值,要獲得訪問(wèn)數(shù)組元素的新的偏移量,則需要使用 s。
arrayIndexScale
返回給定數(shù)組類(lèi)的每個(gè)元素在內(nèi)存中的 scale(所占用的字節(jié))。然而對(duì)于“narrow”類(lèi)型的數(shù)組,類(lèi)似 getByte(Object, int)的訪問(wèn)方法一般不會(huì)獲得正確的結(jié)果,所以這些類(lèi)返回的 scale 會(huì)是 0。
下邊用代碼解釋?zhuān)?/p>
public?class?MyObj?{????int?objField=10;????static?int?clsField=10;????int[]?array={10,20,30,40,50};????static?Unsafe?U;????static?{????????try?{ ????????????init(); ????????}?catch?(NoSuchFieldException?e)?{ ????????????e.printStackTrace(); ????????}?catch?(IllegalAccessException?e)?{ ????????????e.printStackTrace(); ????????} ????}????public?static?void?init()?throws?NoSuchFieldException,?IllegalAccessException?{ ????????Field?f=?Unsafe.class.getDeclaredField("theUnsafe"); ????????f.setAccessible(true); ????????U=?(Unsafe)?f.get(null); ????} ????...... }
定義一個(gè)類(lèi)包含成員變量 objField、類(lèi)變量 clsField、成員數(shù)組 array 用于實(shí)驗(yàn)。要獲取正確的結(jié)果,必須滿(mǎn)足注釋里的三個(gè)條件之一:
1、offset 通過(guò) objectFieldOffset 方法獲取的類(lèi)的某一字段的偏移量,并且關(guān)聯(lián)的對(duì)象 o 是該類(lèi)的兼容對(duì)象(對(duì)象 o 所在的類(lèi)必須是該類(lèi)或該類(lèi)的子類(lèi))
public?class?MyObjChild?extends?MyObj?{????int?anthor; }
????static?void?getObjFieldVal()?throws?NoSuchFieldException?{ ????????Field?field=MyObj.class.getDeclaredField("objField");????????long?offset=?U.objectFieldOffset(field); ????????MyObj?obj=new?MyObj();????????int?val=?U.getInt(obj,offset); ????????System.out.println("1.\t"+(val==10)); ????????MyObjChild?child=new?MyObjChild();????????int?corVal1=?U.getInt(child,offset); ????????System.out.println("2.\t"+(corVal1==10)); ????????Field?fieldChild=MyObj.class.getDeclaredField("objField");????????long?offsetChild=?U.objectFieldOffset(fieldChild); ????????System.out.println("3.\t"+(offset==offsetChild));????????int?corVal2=?U.getInt(obj,offsetChild); ????????System.out.println("4.\t"+(corVal2==10));????????short?errVal1=U.getShort(obj,offset); ????????System.out.println("5.\t"+(errVal1==10));????????int?errVal2=U.getInt("abcd",offset); ????????System.out.println("6.\t"+errVal2); ????}
輸出結(jié)果為:
truetruetruetruetrue-223271518
第一個(gè)參數(shù) o 和 offset 都是從 MyObj 獲取的,所以返回 true。
第二個(gè)參數(shù) o 是 MyObjChild 的實(shí)例,MyObjChild 是 MyObj 的子類(lèi),對(duì)象 o 是 MyObj 的兼容實(shí)例,所以返回 true。這從側(cè)面說(shuō)明在虛擬機(jī)中子類(lèi)的實(shí)例的內(nèi)存結(jié)構(gòu)繼承了父類(lèi)的實(shí)例的內(nèi)存結(jié)構(gòu)。
第三個(gè)比較子類(lèi)和父類(lèi)中獲取的字段偏移量是否相同,返回 true 說(shuō)明是一樣的,既然是一樣的,第四個(gè)自然就返回 true。
這里重點(diǎn)說(shuō)一下第五個(gè),objField 是一個(gè) int 類(lèi)型,占四個(gè)字節(jié),其值為 10,二進(jìn)制為 00000000 00000000 00000000 00001010。Intel 處理器讀取內(nèi)存使用的是小端(Little-Endian)模式,在使用 Intel 處理器的機(jī)器的內(nèi)存中多字節(jié)類(lèi)型是按小端存儲(chǔ)的,即低位在內(nèi)存的低字節(jié)存儲(chǔ),高位在內(nèi)存的高字節(jié)存儲(chǔ),所以 int 10 在內(nèi)存中是(offset 0-3)?00001010 00000000 00000000 00000000。使用 getShort 會(huì)讀取兩個(gè)字節(jié),即 00001010 00000000,獲取的值仍為 10。
但是某些處理器是使用大端(Big-Endian),如 ARM 支持小端和大端,使用此處理器的機(jī)器的內(nèi)存就會(huì)按大端存儲(chǔ)多字節(jié)類(lèi)型,與小端相反,此模式下低位在內(nèi)存的高字節(jié)存儲(chǔ),高位在內(nèi)存的低字節(jié)存儲(chǔ),所以 int 10 在內(nèi)存中是(offset 0-3)00000000 00000000 00000000 00001010。在這種情況下,getShort 獲取的值將會(huì)是 0。
不同的機(jī)器可能產(chǎn)生不一樣的結(jié)果,基于此情況,如果字段是 int 類(lèi)型,但需要一個(gè) short 類(lèi)型,也不應(yīng)該調(diào)用 getShort,而應(yīng)該調(diào)用 getInt,然后強(qiáng)制轉(zhuǎn)換成 short。此外,如果調(diào)用 getLong,該方法返回的值一定不是 10。就像方法注釋所說(shuō),調(diào)用該類(lèi)型方法時(shí),要保證方法的返回值和字段的值是同一種類(lèi)型。
第五個(gè)測(cè)試獲取非 MyObj 實(shí)例的偏移位置的值,這種情況下代碼本身并不會(huì)報(bào)錯(cuò),但獲取到的值并非該字段的值(未定義的值)
2、offset 通過(guò) staticFieldOffset 方法獲取的類(lèi)的某一字段的偏移量,o 是通過(guò) staticFieldBase 方法獲取的對(duì)象
????static?void?getClsFieldVal()?throws?NoSuchFieldException?{ ????????Field?field=MyObj.class.getDeclaredField("clsField");????????long?offset=?U.staticFieldOffset(field); ????????Object?obj=U.staticFieldBase(field);????????int?val1=U.getInt(MyObj.class,offset); ????????System.out.println("1.\t"+(val1==10));????????int?val2=U.getInt(obj,offset); ????????System.out.println("2.\t"+(val2==10)); ????}
輸出結(jié)果:
truetrue
獲取靜態(tài)字段的值,有兩個(gè)方法:staticFieldBase 獲取字段所在的對(duì)象,靜態(tài)字段附著于 Class 本身(java.lang.Class 的實(shí)例),該方法返回的其實(shí)就是該類(lèi)本身,本例中是 MyObj.class。
3、如果 o 引用的是數(shù)組,則 offset 的值為 B+N*S,其中 N 是數(shù)組的合法的下標(biāo),B 是通過(guò) arrayBaseOffset 方法從該數(shù)組類(lèi)獲取的,S 是通過(guò) arrayIndexScale 方法從該數(shù)組類(lèi)獲取的
????static?void?getArrayVal(int?index,int?expectedVal)?throws?NoSuchFieldException?{????????int?base=U.arrayBaseOffset(int[].class);????????int?scale=U.arrayIndexScale(int[].class); ????????MyObj?obj=new?MyObj(); ????????Field?field=MyObj.class.getDeclaredField("array");????????long?offset=?U.objectFieldOffset(field);????????int[]?array=?(int[])?U.getObject(obj,offset);????????int?val1=U.getInt(array,(long)base+index*scale); ????????System.out.println("1.\t"+(val1==expectedVal));????????int?val2=U.getInt(obj.array,(long)base+index*scale); ????????System.out.println("2.\t"+(val2==expectedVal)); ????}
getArrayVal(2,30);
輸出結(jié)果:
truetrue
獲取數(shù)組的值以及獲取數(shù)組中某下標(biāo)的值。獲取數(shù)組某一下標(biāo)的偏移量有一個(gè)計(jì)算公式 B+N*S,B 是數(shù)組元素在數(shù)組中的基準(zhǔn)偏移量,S 是每個(gè)元素占用的字節(jié)數(shù),N 是數(shù)組元素的下標(biāo)。
有個(gè)要注意的地方,上面例子中方法內(nèi)的數(shù)組的 offset 和 base 是兩個(gè)完全不同的偏移量,offset 是數(shù)組 array 在對(duì)象 obj 中的偏移量,base 是數(shù)組元素在數(shù)組中的基準(zhǔn)偏移量,這兩個(gè)值沒(méi)有任何聯(lián)系,不能通過(guò) offset 推導(dǎo)出 base。
getInt 的參數(shù) o 可以是 null,在這種情況下,其和方法 getInt(long) 就是一樣的了,offset 就不是表示相對(duì)的偏移地址了,而是表示內(nèi)存中的絕對(duì)地址。操作系統(tǒng)中,一個(gè)進(jìn)程是不能訪問(wèn)其他進(jìn)程的內(nèi)存的,所以傳入 getInt 中的絕對(duì)地址必須是當(dāng)前 JVM 管理的內(nèi)存地址,否則進(jìn)程會(huì)退出。
?
下一個(gè)方法,將值存儲(chǔ)到 Java 變量中:
????public?native?void?putInt(Object?o,?long?offset,?int?x);
前兩個(gè)參數(shù)會(huì)被解釋成 Java 變量(字段或數(shù)組)的引用,
參數(shù)給定的值會(huì)被存儲(chǔ)到該變量,變量的類(lèi)型必須和方法參數(shù)的類(lèi)型一致
參數(shù) o 是變量關(guān)聯(lián)的 Java 堆對(duì)象,可以為 null
參數(shù) offset 代表該變量在該對(duì)象的位置,如果 o 是 null 則是內(nèi)存的絕對(duì)地址
修改指定位置的內(nèi)存,測(cè)試代碼:
????static?void?setObjFieldVal(int?val)?throws?NoSuchFieldException?{ ????????Field?field=MyObj.class.getDeclaredField("objField");????????long?offset=?U.objectFieldOffset(field); ????????MyObj?obj=new?MyObj(); ????????U.putInt(obj,offset,val);????????int?getVal=?U.getInt(obj,offset); ????????System.out.println(val==getVal); ????????U.putLong(obj,offset,val); ????????Field?fieldArray=MyObj.class.getDeclaredField("array");????????long?offsetArray=?U.objectFieldOffset(fieldArray);//????????int[]?array=?(int[])?U.getObject(obj,offsetArray);//????????for(int?i=0;i<array.length;i++){//????????????System.out.println(array[i]);//????????} ????}
objField 是 int 類(lèi)型,通過(guò) putInt 修改 objField 的值可以正常修改,結(jié)果打印 true。然后使用 putLong 修改 objField 的值,這個(gè)修改操作本身也不會(huì)報(bào)錯(cuò),但是 objField 并不是 long 類(lèi)型,這樣修改會(huì)導(dǎo)致其它程序錯(cuò)誤,它不僅修改了 objField 的內(nèi)存值,還修改了 objField 之后四個(gè)字節(jié)的內(nèi)存值。
在這個(gè)例子中,objField 后面八個(gè)字節(jié)存儲(chǔ)的是 array 字段所表示的數(shù)組對(duì)象的偏移位置,但是被修改了,如果后面的代碼嘗試訪問(wèn) array 字段就會(huì)出錯(cuò)。修改其它字段(array、clsField)也是一樣的,只要按之前的方法獲取字段的偏移位置,使用與字段類(lèi)型一致的 put 方法就可以。
下面的方法都是差不多的,只是針對(duì)不同的數(shù)據(jù)類(lèi)型或者兼容 1.4 的字節(jié)碼:
????public?native?void?putObject(Object?o,?long?offset,?Object?x);????public?native?boolean?getBoolean(Object?o,?long?offset);????public?native?void????putBoolean(Object?o,?long?offset,?boolean?x);????public?native?byte????getByte(Object?o,?long?offset);????public?native?void????putByte(Object?o,?long?offset,?byte?x);????public?native?short???getShort(Object?o,?long?offset);? ????public?native?void????putShort(Object?o,?long?offset,?short?x);????public?native?char????getChar(Object?o,?long?offset);????public?native?void????putChar(Object?o,?long?offset,?char?x);????public?native?long????getLong(Object?o,?long?offset);????public?native?void????putLong(Object?o,?long?offset,?long?x);????public?native?float???getFloat(Object?o,?long?offset);????public?native?void????putFloat(Object?o,?long?offset,?float?x);????public?native?double??getDouble(Object?o,?long?offset);????public?native?void????putDouble(Object?o,?long?offset,?double?x);????@Deprecated ????public?int?getInt(Object?o,?int?offset)?{????????return?getInt(o,?(long)offset); ????}??? ????@Deprecated ????public?void?putInt(Object?o,?int?offset,?int?x)?{ ????????putInt(o,?(long)offset,?x); ????}??? ????@Deprecated ????public?Object?getObject(Object?o,?int?offset)?{????????return?getObject(o,?(long)offset); ????}??? ????@Deprecated ????public?void?putObject(Object?o,?int?offset,?Object?x)?{ ????????putObject(o,?(long)offset,?x); ????}???? ????@Deprecated ????public?boolean?getBoolean(Object?o,?int?offset)?{????????return?getBoolean(o,?(long)offset); ????}??? ????@Deprecated ????public?void?putBoolean(Object?o,?int?offset,?boolean?x)?{ ????????putBoolean(o,?(long)offset,?x); ????}???? ????@Deprecated ????public?byte?getByte(Object?o,?int?offset)?{????????return?getByte(o,?(long)offset); ????}???? ????@Deprecated ????public?void?putByte(Object?o,?int?offset,?byte?x)?{ ????????putByte(o,?(long)offset,?x); ????}???? ????@Deprecated ????public?short?getShort(Object?o,?int?offset)?{????????return?getShort(o,?(long)offset); ????}??? ????@Deprecated ????public?void?putShort(Object?o,?int?offset,?short?x)?{ ????????putShort(o,?(long)offset,?x); ????}???? ????@Deprecated ????public?char?getChar(Object?o,?int?offset)?{????????return?getChar(o,?(long)offset); ????}???? ????@Deprecated ????public?void?putChar(Object?o,?int?offset,?char?x)?{ ????????putChar(o,?(long)offset,?x); ????}???? ????@Deprecated ????public?long?getLong(Object?o,?int?offset)?{????????return?getLong(o,?(long)offset); ????}???? ????@Deprecated ????public?void?putLong(Object?o,?int?offset,?long?x)?{ ????????putLong(o,?(long)offset,?x); ????}???? ????@Deprecated ????public?float?getFloat(Object?o,?int?offset)?{????????return?getFloat(o,?(long)offset); ????}???? ????@Deprecated ????public?void?putFloat(Object?o,?int?offset,?float?x)?{ ????????putFloat(o,?(long)offset,?x); ????}???? ????@Deprecated ????public?double?getDouble(Object?o,?int?offset)?{????????return?getDouble(o,?(long)offset); ????}???? ????@Deprecated ????public?void?putDouble(Object?o,?int?offset,?double?x)?{ ????????putDouble(o,?(long)offset,?x); ????}
下面的方法和上面的也類(lèi)似,只是這些方法只有一個(gè)參數(shù),即內(nèi)存絕對(duì)地址,這些方法不需要 Java 對(duì)象地址作為基準(zhǔn)地址,所以它們可以作用于本地方法區(qū):
????//獲取內(nèi)存地址的值,如果地址是?0?或者不是指向通過(guò)?allocateMemory?方法獲取的內(nèi)存塊,則結(jié)果是未知的 ???? ????public?native?byte????getByte(long?address);????//將一個(gè)值寫(xiě)入內(nèi)存,如果地址是?0?或者不是指向通過(guò)?allocateMemory?方法獲取的內(nèi)存塊,則結(jié)果是未知的 ????public?native?void????putByte(long?address,?byte?x);????public?native?short???getShort(long?address);????public?native?void????putShort(long?address,?short?x);????public?native?char????getChar(long?address);????public?native?void????putChar(long?address,?char?x);????public?native?int?????getInt(long?address);????public?native?void????putInt(long?address,?int?x);????public?native?long????getLong(long?address);? ????public?native?void????putLong(long?address,?long?x);????public?native?float???getFloat(long?address);? ????public?native?void????putFloat(long?address,?float?x);?? ????public?native?double??getDouble(long?address);?? ????public?native?void????putDouble(long?address,?double?x);
這里提到一個(gè)方法 allocateMemory,它是用于分配本地內(nèi)存的??纯春捅镜貎?nèi)存有關(guān)的幾個(gè)方法:
????///包裝malloc,realloc,free ????/** ?????*?分配指定大小的一塊本地內(nèi)存。分配的這塊內(nèi)存不會(huì)初始化,它們的內(nèi)容通常是沒(méi)用的數(shù)據(jù) ?????*?返回的本地指針不會(huì)是?0,并且該內(nèi)存塊是連續(xù)的。調(diào)用?freeMemory?方法可以釋放此內(nèi)存,調(diào)用 ?????*?reallocateMemory?方法可以重新分配 ?????*/ ????public?native?long?allocateMemory(long?bytes);????/** ?????*?重新分配一塊指定大小的本地內(nèi)存,超出老內(nèi)存塊的字節(jié)不會(huì)被初始化,它們的內(nèi)容通常是沒(méi)用的數(shù)據(jù) ?????*?當(dāng)且僅當(dāng)請(qǐng)求的大小為?0?時(shí),該方法返回的本地指針會(huì)是?0。 ?????*?該內(nèi)存塊是連續(xù)的。調(diào)用?freeMemory?方法可以釋放此內(nèi)存,調(diào)用?reallocateMemory?方法可以重新分配 ?????*?參數(shù)?address?可以是?null,這種情況下會(huì)分配新內(nèi)存(和?allocateMemory?一樣) ?????*/ ????public?native?long?reallocateMemory(long?address,?long?bytes);????/**? ?????*?將給定的內(nèi)存塊的所有字節(jié)設(shè)置成固定的值(通常是?0) ?????*?該方法通過(guò)兩個(gè)參數(shù)確定內(nèi)存塊的基準(zhǔn)地址,就像在?getInt(Object,long)?中討論的,它提供了?double-register?地址模型 ?????*?如果引用的對(duì)象是?null,?則?offset?會(huì)被當(dāng)成絕對(duì)基準(zhǔn)地址 ?????*?該寫(xiě)入操作是按單元寫(xiě)入的,單元的字節(jié)大小由地址和長(zhǎng)度參數(shù)決定,每個(gè)單元的寫(xiě)入是原子性的。如果地址和長(zhǎng)度都是?8?的倍數(shù),則一個(gè)單元為?long ?????*?型(一個(gè)單元?8?個(gè)字節(jié));如果地址和長(zhǎng)度都是?4?的倍數(shù),則一個(gè)單元為?int?型(一個(gè)單元?4?個(gè)字節(jié)); ?????*?如果地址和長(zhǎng)度都是?2?的倍數(shù),則一個(gè)單元為?short?型(一個(gè)單元?2?個(gè)字節(jié)); ?????*/ ????public?native?void?setMemory(Object?o,?long?offset,?long?bytes,?byte?value);????//將給定的內(nèi)存塊的所有字節(jié)設(shè)置成固定的值(通常是?0) ????//就像在?getInt(Object,long)?中討論的,該方法提供?single-register?地址模型 ????public?void?setMemory(long?address,?long?bytes,?byte?value)?{ ????????setMemory(null,?address,?bytes,?value); ????}????//復(fù)制指定內(nèi)存塊的字節(jié)到另一內(nèi)存塊 ????//該方法的兩個(gè)基準(zhǔn)地址分別由兩個(gè)參數(shù)決定 ????public?native?void?copyMemory(Object?srcBase,?long?srcOffset, ??????????????????????????????????Object?destBase,?long?destOffset,??????????????????????????????????long?bytes);????//復(fù)制指定內(nèi)存塊的字節(jié)到另一內(nèi)存塊,但使用?single-register?地址模型 ????? ????public?void?copyMemory(long?srcAddress,?long?destAddress,?long?bytes)?{ ????????copyMemory(null,?srcAddress,?null,?destAddress,?bytes); ????}????//釋放通過(guò)?allocateMemory?或者?reallocateMemory?獲取的內(nèi)存,如果參數(shù)?address?是?null,則不做任何處理 ????public?native?void?freeMemory(long?address);
allocateMemory、reallocateMemory、freeMemory 與 setMemory 分別是對(duì) C 函數(shù) malloc、realloc、free 和 memset 的封裝,這樣該類(lèi)就提供了動(dòng)態(tài)獲取/釋放本地方法區(qū)內(nèi)存的功能。
malloc 用于分配一個(gè)全新的未使用的連續(xù)內(nèi)存,但該內(nèi)存不會(huì)初始化,即不會(huì)被清零;
realloc 用于內(nèi)存的縮容或擴(kuò)容,有兩個(gè)參數(shù),從 malloc 返回的地址和要調(diào)整的大小,該函數(shù)和 malloc 一樣,不會(huì)初始化,它能保留之前放到內(nèi)存里的值,很適合用于擴(kuò)容;
free 用于釋放內(nèi)存,該方法只有一個(gè)地址參數(shù),那它如何知道要釋放多少個(gè)字節(jié)呢?其實(shí)在 malloc 分配內(nèi)存的時(shí)候會(huì)多分配 4 個(gè)字節(jié)用于存放該塊的長(zhǎng)度,比如 malloc(10) 其實(shí)會(huì)花費(fèi) 14 個(gè)字節(jié)。理論上講能分配的最大內(nèi)存是 4G(2^32-1)。在 hotspot 虛擬機(jī)的設(shè)計(jì)中,數(shù)組對(duì)象也有 4 個(gè)字節(jié)用于存放數(shù)組長(zhǎng)度,那么在 hotspot 中,數(shù)組的最大長(zhǎng)度就是 2^32-1,這樣 free 函數(shù)只要讀取前 4 個(gè)字節(jié)就知道要釋放多少內(nèi)存了(10+4);
memset? 一般用于初始化內(nèi)存,可以設(shè)置初始化內(nèi)存的值,一般初始值會(huì)設(shè)置成 0,即清零操作。
來(lái)一個(gè)簡(jiǎn)單的例子,申請(qǐng)內(nèi)存-寫(xiě)入內(nèi)存-讀取內(nèi)存-釋放內(nèi)存:
long?address=U.allocateMemory(10); U.setMemory(address,10,(byte)1);/** ?*?1的二進(jìn)制碼為00000001,int為四個(gè)字節(jié),U.getInt將讀取四個(gè)字節(jié), ?*?讀取的字節(jié)為00000001?00000001?00000001?00000001 ?*/int?i=0b00000001000000010000000100000001; System.out.println(i==U.getInt(address)); U.freeMemory(address);
?
接下來(lái)看看獲取類(lèi)變量相關(guān)信息的幾個(gè)方法:
????///?random?queries ????///?隨機(jī)搜索,對(duì)象是放在一塊連續(xù)的內(nèi)存空間中,所以是支持隨機(jī)搜索的 ????/** ?????*?staticFieldOffset,objectFieldOffset,arrayBaseOffset?方法的返回值不會(huì)是該常量(-1) ?????*/ ????public?static?final?int?INVALID_FIELD_OFFSET???=?-1;????/** ?????*?返回字段的偏移量,32?字節(jié) ?????*?從?1.4.1?開(kāi)始,對(duì)于靜態(tài)字段,請(qǐng)使用?staticFieldOffset?方法,非靜態(tài)字段使用?objectFieldOffset?方法獲取 ?????*/ ????@Deprecated ????public?int?fieldOffset(Field?f)?{????????if?(Modifier.isStatic(f.getModifiers()))????????????return?(int)?staticFieldOffset(f);????????else ????????????return?(int)?objectFieldOffset(f); ????}????/** ?????*?返回用于訪問(wèn)靜態(tài)字段的基準(zhǔn)地址 ?????*?從?1.4.1?開(kāi)始,要獲取訪問(wèn)指定字段的基準(zhǔn)地址,請(qǐng)使用?staticFieldBase(Field) ?????*?該方法僅能作用于把所有靜態(tài)字段放在一起的?JVM?實(shí)現(xiàn) ?????*/ ????@Deprecated ????public?Object?staticFieldBase(Class<?>?c)?{ ????????Field[]?fields?=?c.getDeclaredFields();????????for?(int?i?=?0;?i?<?fields.length;?i++)?{????????????if?(Modifier.isStatic(fields[i].getModifiers()))?{????????????????return?staticFieldBase(fields[i]); ????????????} ????????}????????return?null; ????}????/** ?????*?返回給定的字段在該類(lèi)的偏移地址 ?????* ?????*?對(duì)于任何給定的字段,該方法總是返回相同的值;同一個(gè)類(lèi)的不同字段總是返回不同的值 ?????* ?????*?從?1.4.1?開(kāi)始,字段的偏移以?long?表示,雖然?Sun?的?JVM?只使用了?32?位,但是那些將靜態(tài)字段存儲(chǔ)到絕對(duì)地址的?JVM?實(shí)現(xiàn) ?????*?需要使用?long?類(lèi)型的偏移量,通過(guò)?getXX(null,long)?獲取字段值,為了保持代碼遷移到?64?位平臺(tái)上?JVM?的優(yōu)良性, ?????*?必須保持靜態(tài)字段偏移量的所有比特位 ?????*/ ????public?native?long?staticFieldOffset(Field?f);????/** ?????*?很難想象?JVM?需要使用這么多比特位來(lái)編碼非數(shù)組對(duì)象的偏移量,它們只需要很少的比特位就可以了(有誰(shuí)看過(guò)有100個(gè)成員變量的類(lèi)么? ?????*?一個(gè)字節(jié)能表示?256?個(gè)成員變量), ?????*?為了和該類(lèi)的其他方法保持一致,所以該方法也返回?long?類(lèi)型 ?????* ?????*/ ????public?native?long?objectFieldOffset(Field?f);????/** ?????*?獲取指定靜態(tài)字段的位置,和?staticFieldOffset?一起使用 ?????*?獲取該靜態(tài)字段所在的"對(duì)象",這個(gè)"對(duì)象"可通過(guò)類(lèi)似?getInt(Object,long)?的方法訪問(wèn) ?????*?該"對(duì)象"可能是?null,并且引用的可能是對(duì)象的"cookie"(此處cookie具體含義未知,沒(méi)有找到相關(guān)資料),不保證是真正的對(duì)象,該"對(duì)象"只能當(dāng)作此類(lèi)中?put?和?get?方法的參數(shù), ?????*?其他情況下不應(yīng)該使用它 ?????*/ ????public?native?Object?staticFieldBase(Field?f);????/** ?????*?檢查給定的類(lèi)是否需要初始化,它通常和?staticFieldBase?方法一起使用 ?????*?只有當(dāng)?ensureClassInitialized?方法不產(chǎn)生任何影響時(shí)才會(huì)返回?false ?????*/ ????public?native?boolean?shouldBeInitialized(Class<?>?c);????/** ?????*?確保給定的類(lèi)已被初始化,它通常和?staticFieldBase?方法一起使用 ?????*/ ????public?native?void?ensureClassInitialized(Class<?>?c);????/** ?????*?返回給定數(shù)組類(lèi)第一個(gè)元素在內(nèi)存中的偏移量,如果?arrayIndexScale?方法返回非0值,要獲得訪問(wèn)數(shù)組元素的新的偏移量, ?????*?需要使用?scale ?????*/ ????public?native?int?arrayBaseOffset(Class<?>?arrayClass);????/**?The?value?of?{@code?arrayBaseOffset(boolean[].class)}?*/ ????public?static?final?int?ARRAY_BOOLEAN_BASE_OFFSET ????????????=?theUnsafe.arrayBaseOffset(boolean[].class);????/**?The?value?of?{@code?arrayBaseOffset(byte[].class)}?*/ ????public?static?final?int?ARRAY_BYTE_BASE_OFFSET ????????????=?theUnsafe.arrayBaseOffset(byte[].class);????/**?The?value?of?{@code?arrayBaseOffset(short[].class)}?*/ ????public?static?final?int?ARRAY_SHORT_BASE_OFFSET ????????????=?theUnsafe.arrayBaseOffset(short[].class);????/**?The?value?of?{@code?arrayBaseOffset(char[].class)}?*/ ????public?static?final?int?ARRAY_CHAR_BASE_OFFSET ????????????=?theUnsafe.arrayBaseOffset(char[].class);????/**?The?value?of?{@code?arrayBaseOffset(int[].class)}?*/ ????public?static?final?int?ARRAY_INT_BASE_OFFSET ????????????=?theUnsafe.arrayBaseOffset(int[].class);????/**?The?value?of?{@code?arrayBaseOffset(long[].class)}?*/ ????public?static?final?int?ARRAY_LONG_BASE_OFFSET ????????????=?theUnsafe.arrayBaseOffset(long[].class);????/**?The?value?of?{@code?arrayBaseOffset(float[].class)}?*/ ????public?static?final?int?ARRAY_FLOAT_BASE_OFFSET ????????????=?theUnsafe.arrayBaseOffset(float[].class);????/**?The?value?of?{@code?arrayBaseOffset(double[].class)}?*/ ????public?static?final?int?ARRAY_DOUBLE_BASE_OFFSET ????????????=?theUnsafe.arrayBaseOffset(double[].class);????/**?The?value?of?{@code?arrayBaseOffset(Object[].class)}?*/ ????public?static?final?int?ARRAY_OBJECT_BASE_OFFSET ????????????=?theUnsafe.arrayBaseOffset(Object[].class);????/** ?????*?返回給定數(shù)組類(lèi)的每個(gè)元素在內(nèi)存中的?scale(所占用的字節(jié))。然而對(duì)于"narrow"類(lèi)型的數(shù)組,類(lèi)似?getByte(Object,?int)?的訪問(wèn)方法 ?????*?一般不會(huì)獲得正確的結(jié)果,所以這些類(lèi)返回的?scale?會(huì)是?0 ?????*?(本人水平有限,此處narrow類(lèi)型不知道具體含義,不了解什么時(shí)候此方法會(huì)返回0) ?????*/ ????public?native?int?arrayIndexScale(Class<?>?arrayClass);????/**?The?value?of?{@code?arrayIndexScale(boolean[].class)}?*/ ????public?static?final?int?ARRAY_BOOLEAN_INDEX_SCALE ????????????=?theUnsafe.arrayIndexScale(boolean[].class);????/**?The?value?of?{@code?arrayIndexScale(byte[].class)}?*/ ????public?static?final?int?ARRAY_BYTE_INDEX_SCALE ????????????=?theUnsafe.arrayIndexScale(byte[].class);????/**?The?value?of?{@code?arrayIndexScale(short[].class)}?*/ ????public?static?final?int?ARRAY_SHORT_INDEX_SCALE ????????????=?theUnsafe.arrayIndexScale(short[].class);????/**?The?value?of?{@code?arrayIndexScale(char[].class)}?*/ ????public?static?final?int?ARRAY_CHAR_INDEX_SCALE ????????????=?theUnsafe.arrayIndexScale(char[].class);????/**?The?value?of?{@code?arrayIndexScale(int[].class)}?*/ ????public?static?final?int?ARRAY_INT_INDEX_SCALE ????????????=?theUnsafe.arrayIndexScale(int[].class);????/**?The?value?of?{@code?arrayIndexScale(long[].class)}?*/ ????public?static?final?int?ARRAY_LONG_INDEX_SCALE ????????????=?theUnsafe.arrayIndexScale(long[].class);????/**?The?value?of?{@code?arrayIndexScale(float[].class)}?*/ ????public?static?final?int?ARRAY_FLOAT_INDEX_SCALE ????????????=?theUnsafe.arrayIndexScale(float[].class);????/**?The?value?of?{@code?arrayIndexScale(double[].class)}?*/ ????public?static?final?int?ARRAY_DOUBLE_INDEX_SCALE ????????????=?theUnsafe.arrayIndexScale(double[].class);????/**?The?value?of?{@code?arrayIndexScale(Object[].class)}?*/ ????public?static?final?int?ARRAY_OBJECT_INDEX_SCALE ????????????=?theUnsafe.arrayIndexScale(Object[].class);
上面的一些方法之前已經(jīng)提到過(guò),注釋也說(shuō)的比較明白, 說(shuō)一下 shouldBeInitialized 和 ensureClassInitialized,shouldBeInitialized 判斷類(lèi)是否已初始化,ensureClassInitialized 執(zhí)行初始化。有個(gè)概念需要了解,虛擬機(jī)加載類(lèi)包括加載和鏈接階段,加載階段只是把類(lèi)加載進(jìn)內(nèi)存,鏈接階段會(huì)驗(yàn)證加載的代碼的合法性,并初始化靜態(tài)字段和靜態(tài)塊;shouldBeInitialized 就是檢查鏈接階段有沒(méi)有執(zhí)行。
????static?void?clsInitialized()?throws?NoSuchFieldException?{ ????????System.out.println(U.shouldBeInitialized(MyObj.class)); ????????System.out.println(U.shouldBeInitialized(MyObjChild.class)); ????????U.ensureClassInitialized(MyObjChild.class); ????????System.out.println(U.shouldBeInitialized(MyObjChild.class)); ????}
public?class?MyObjChild?extends?MyObj?{????static?int?f1=1;????final?static?int?f2=1;????static?{ ????????f1=2; ????????System.out.println("MyObjChild?init"); ????} }
輸出:
false?true?MyObjChild?init? false
第一行輸出 false 是因?yàn)槲疫@個(gè)代碼(包括 main 方法)是在 MyObj 類(lèi)里寫(xiě)的,執(zhí)行 main 的時(shí)候,MyObj 已經(jīng)加載并初始化了。調(diào)用 U.shouldBeInitialized(MyObjChild.class) 只會(huì)加載 MyObjChild.class,但不會(huì)初始化,執(zhí)行 ensureClassInitialized 才會(huì)初始化。
????static?void?clsInitialized2()?throws?NoSuchFieldException?{ ????????Field?f1=MyObjChild.class.getDeclaredField("f1"); ????????Field?f2=MyObjChild.class.getDeclaredField("f2");????????long?f1Offset=?U.staticFieldOffset(f1);????????long?f2Offset=?U.staticFieldOffset(f2);????????int?f1Val=U.getInt(MyObjChild.class,f1Offset);????????int?f2Val=U.getInt(MyObjChild.class,f2Offset); ????????System.out.println("1.\t"+(f1Val==0)); ????????System.out.println("2.\t"+(f2Val==1)); ????????U.ensureClassInitialized(MyObjChild.class); ????????f1Val=U.getInt(MyObjChild.class,f1Offset); ????????System.out.println("3.\t"+(f1Val==2)); ????}
輸出:
1.true2.true?MyObjChild?init3.true
f1 是 static int,f2 是 final static int,因?yàn)?f2 是 final,它的值在編譯期就決定了,存放在類(lèi)的常量表里,所以即使還沒(méi)有初始化它的值就是 1。
?
????/** ?????*?獲取本地指針?biāo)加玫淖止?jié)大小,值為?4?或者?8。其他基本類(lèi)型的大小由其內(nèi)容決定 ?????*/ ????public?native?int?addressSize();????/**?The?value?of?{@code?addressSize()}?*/ ????public?static?final?int?ADDRESS_SIZE?=?theUnsafe.addressSize();????/** ?????*?本地內(nèi)存頁(yè)大小,值為?2?的?N?次方 ?????*/ ????public?native?int?pageSize();
addressSize 返回指針的大小,32 位虛擬機(jī)返回 4,64 位虛擬機(jī)默認(rèn)返回 8,開(kāi)啟指針壓縮功能(-XX:-UseCompressedOops)則返回 4?;绢?lèi)型不是用指針表示的,它是直接存儲(chǔ)的值。一般情況下,我們會(huì)說(shuō)在 Java 中,基本類(lèi)型是值傳遞,對(duì)象是引用傳遞。Java 官方的表述是在任何情況下 Java 都是值傳遞?;绢?lèi)型是傳遞值本身,對(duì)象類(lèi)型是傳遞指針的值。
?
????///?random?trusted?operations?from?JNI: ????///?JNI信任的操作 ????/** ?????*?告訴虛擬機(jī)定義一個(gè)類(lèi),加載類(lèi)不做安全檢查,默認(rèn)情況下,參數(shù)類(lèi)加載器(ClassLoader)和保護(hù)域(ProtectionDomain)來(lái)自調(diào)用者類(lèi) ?????*/ ????public?native?Class<?>?defineClass(String?name,?byte[]?b,?int?off,?int?len, ???????????????????????????????????????ClassLoader?loader, ???????????????????????????????????????ProtectionDomain?protectionDomain);????/** ?????*?定義一個(gè)匿名類(lèi),這里說(shuō)的和我們代碼里寫(xiě)的匿名內(nèi)部類(lèi)不是一個(gè)東西。 ?????*?(可以參考知乎上的一個(gè)問(wèn)答?https://www.zhihu.com/question/51132462) ?????*/ ????public?native?Class<?>?defineAnonymousClass(Class<?>?hostClass,?byte[]?data,?Object[]?cpPatches);????/**? ?????*?分配實(shí)例的內(nèi)存空間,但不會(huì)執(zhí)行構(gòu)造函數(shù)。如果沒(méi)有執(zhí)行初始化,則會(huì)執(zhí)行初始化 ?????*/ ????public?native?Object?allocateInstance(Class<?>?cls) ????????????throws?InstantiationException;????/**?Lock?the?object.??It?must?get?unlocked?via?{@link?#monitorExit}. ?????* ?????*?獲取對(duì)象內(nèi)置鎖(即?synchronized?關(guān)鍵字獲取的鎖),必須通過(guò)?monitorExit?方法釋放鎖 ?????*?(synchronized?代碼塊在編譯后會(huì)產(chǎn)生兩個(gè)指令:monitorenter,monitorexit) ?????*/ ????public?native?void?monitorEnter(Object?o);????/** ?????*?Unlock?the?object.??It?must?have?been?locked?via?{@link ?????*?#monitorEnter}. ?????*?釋放鎖 ?????*/ ????public?native?void?monitorExit(Object?o);????/** ?????*?嘗試獲取對(duì)象內(nèi)置鎖,通過(guò)返回?true?和?false?表示是否成功獲取鎖 ?????*/ ????public?native?boolean?tryMonitorEnter(Object?o);????/**?Throw?the?exception?without?telling?the?verifier. ?????*?不通知驗(yàn)證器(verifier)直接拋出異常(此處?verifier?具體含義未知,沒(méi)有找到相關(guān)資料) ?????*/ ????public?native?void?throwException(Throwable?ee);
allocateInstance 方法的測(cè)試
public?class?MyObjChild?extends?MyObj?{????static?int?f1=1;????int?f2=1;????static?{ ????????f1=2; ????????System.out.println("MyObjChild?init"); ????}????public?MyObjChild(){ ????????f2=2; ????????System.out.println("run?construct"); ????} }
???static?void?clsInitialized3()?throws?InstantiationException?{ ????????MyObjChild?myObj=?(MyObjChild)?U.allocateInstance(MyObjChild.class); ????????System.out.println("1.\t"+(MyObjChild.f1==2)); ????????System.out.println("1.\t"+(myObj.f2==0)); ????}
輸出:
MyObjChild?init1.true2.true
可以看到分配對(duì)象的時(shí)候只執(zhí)行了類(lèi)的初始化代碼,沒(méi)有執(zhí)行構(gòu)造函數(shù)。
?
來(lái)看看最重要的 CAS 方法
????/** ?????*?Atomically?update?Java?variable?to?<tt>x</tt>?if?it?is?currently ?????*?holding?<tt>expected</tt>. ?????*?@return?<tt>true</tt>?if?successful ?????* ?????*?如果變量的值為預(yù)期值,則更新變量的值,該操作為原子操作 ?????*?如果修改成功則返回true ?????*/ ????public?final?native?boolean?compareAndSwapObject(Object?o,?long?offset, ?????????????????????????????????????????????????????Object?expected, ?????????????????????????????????????????????????????Object?x);????/** ?????*?Atomically?update?Java?variable?to?<tt>x</tt>?if?it?is?currently ?????*?holding?<tt>expected</tt>. ?????*?@return?<tt>true</tt>?if?successful ?????*/ ????public?final?native?boolean?compareAndSwapInt(Object?o,?long?offset,??????????????????????????????????????????????????int?expected,??????????????????????????????????????????????????int?x);????/** ?????*?Atomically?update?Java?variable?to?<tt>x</tt>?if?it?is?currently ?????*?holding?<tt>expected</tt>. ?????*?@return?<tt>true</tt>?if?successful ?????*/ ????public?final?native?boolean?compareAndSwapLong(Object?o,?long?offset,???????????????????????????????????????????????????long?expected,???????????????????????????????????????????????????long?x);
這幾個(gè)方法應(yīng)該是最常用的方法了,用于實(shí)現(xiàn)原子性的 CAS 操作,這些操作可以避免加鎖,一般情況下,性能會(huì)更好, java.util.concurrent 包下很多類(lèi)就是用的這些 CAS 操作而沒(méi)有用鎖。
????static?void?cas()?throws?NoSuchFieldException?{ ????????Field?field=MyObj.class.getDeclaredField("objField");????????long?offset=?U.objectFieldOffset(field); ????????MyObj?myObj=new?MyObj(); ????????myObj.objField=1; ????????U.compareAndSwapInt(myObj,offset,0,2); ????????System.out.println("1.\t"+(myObj.objField==2)); ????????U.compareAndSwapInt(myObj,offset,1,2); ????????System.out.println("2.\t"+(myObj.objField==2)); ????}
輸出:
1.false2.true
?
????/** ?????*?獲取給定變量的引用值,該操作有?volatile?加載語(yǔ)意,其他方面和?getObject(Object,?long)?一樣 ?????*/ ????public?native?Object?getObjectVolatile(Object?o,?long?offset);????/** ?????*?將引用值寫(xiě)入給定的變量,該操作有?volatile?加載語(yǔ)意,其他方面和?putObject(Object,?long,?Object)?一樣 ?????*/ ????public?native?void????putObjectVolatile(Object?o,?long?offset,?Object?x);????/**?Volatile?version?of?{@link?#getInt(Object,?long)}??*/ ????public?native?int?????getIntVolatile(Object?o,?long?offset);????/**?Volatile?version?of?{@link?#putInt(Object,?long,?int)}??*/ ????public?native?void????putIntVolatile(Object?o,?long?offset,?int?x);????/**?Volatile?version?of?{@link?#getBoolean(Object,?long)}??*/ ????public?native?boolean?getBooleanVolatile(Object?o,?long?offset);????/**?Volatile?version?of?{@link?#putBoolean(Object,?long,?boolean)}??*/ ????public?native?void????putBooleanVolatile(Object?o,?long?offset,?boolean?x);????/**?Volatile?version?of?{@link?#getByte(Object,?long)}??*/ ????public?native?byte????getByteVolatile(Object?o,?long?offset);????/**?Volatile?version?of?{@link?#putByte(Object,?long,?byte)}??*/ ????public?native?void????putByteVolatile(Object?o,?long?offset,?byte?x);????/**?Volatile?version?of?{@link?#getShort(Object,?long)}??*/ ????public?native?short???getShortVolatile(Object?o,?long?offset);????/**?Volatile?version?of?{@link?#putShort(Object,?long,?short)}??*/ ????public?native?void????putShortVolatile(Object?o,?long?offset,?short?x);????/**?Volatile?version?of?{@link?#getChar(Object,?long)}??*/ ????public?native?char????getCharVolatile(Object?o,?long?offset);????/**?Volatile?version?of?{@link?#putChar(Object,?long,?char)}??*/ ????public?native?void????putCharVolatile(Object?o,?long?offset,?char?x);????/**?Volatile?version?of?{@link?#getLong(Object,?long)}??*/ ????public?native?long????getLongVolatile(Object?o,?long?offset);????/**?Volatile?version?of?{@link?#putLong(Object,?long,?long)}??*/ ????public?native?void????putLongVolatile(Object?o,?long?offset,?long?x);????/**?Volatile?version?of?{@link?#getFloat(Object,?long)}??*/ ????public?native?float???getFloatVolatile(Object?o,?long?offset);????/**?Volatile?version?of?{@link?#putFloat(Object,?long,?float)}??*/ ????public?native?void????putFloatVolatile(Object?o,?long?offset,?float?x);????/**?Volatile?version?of?{@link?#getDouble(Object,?long)}??*/ ????public?native?double??getDoubleVolatile(Object?o,?long?offset);????/**?Volatile?version?of?{@link?#putDouble(Object,?long,?double)}??*/ ????public?native?void????putDoubleVolatile(Object?o,?long?offset,?double?x);
這是具有 volatile 語(yǔ)意的 get 和 put方法。volatile 語(yǔ)意為保證不同線程之間的可見(jiàn)行,即一個(gè)線程修改一個(gè)變量之后,保證另一線程能觀測(cè)到此修改。這些方法可以使非 volatile 變量具有 volatile 語(yǔ)意。
?
????/** ?????*?putObjectVolatile(Object,?long,?Object)的另一個(gè)版本(有序的/延遲的),它不保證其他線程能立即看到修改, ?????*?該方法通常只對(duì)底層為?volatile?的變量(或者?volatile?類(lèi)型的數(shù)組元素)有幫助 ?????*/ ????public?native?void????putOrderedObject(Object?o,?long?offset,?Object?x);????/**?Ordered/Lazy?version?of?{@link?#putIntVolatile(Object,?long,?int)}??*/ ????public?native?void????putOrderedInt(Object?o,?long?offset,?int?x);????/**?Ordered/Lazy?version?of?{@link?#putLongVolatile(Object,?long,?long)}?*/ ????public?native?void????putOrderedLong(Object?o,?long?offset,?long?x);
有三類(lèi)很相近的方法:putXx、putXxVolatile 與 putOrderedXx:
putXx 只是寫(xiě)本線程緩存,不會(huì)將其它線程緩存置為失效,所以不能保證其它線程一定看到此次修改;
putXxVolatile 相反,它可以保證其它線程一定看到此次修改;
putOrderedXx 也不保證其它線程一定看到此次修改,但和 putXx 又有區(qū)別,它的注釋上有兩個(gè)關(guān)鍵字:順序性(Ordered)和延遲性(lazy),順序性是指不會(huì)發(fā)生重排序,延遲性是指其它線程不會(huì)立即看到此次修改,只有當(dāng)調(diào)用 putXxVolatile 使才能看到。
?
????/** ?????*?釋放當(dāng)前阻塞的線程。如果當(dāng)前線程沒(méi)有阻塞,則下一次調(diào)用?park?不會(huì)阻塞。這個(gè)操作是"非安全"的 ?????*?是因?yàn)檎{(diào)用者必須通過(guò)某種方式保證該線程沒(méi)有被銷(xiāo)毀 ?????* ?????*/ ????public?native?void?unpark(Object?thread);????/** ?????*?阻塞當(dāng)前線程,當(dāng)發(fā)生如下情況時(shí)返回: ?????*?1、調(diào)用?unpark?方法 ?????*?2、線程被中斷 ?????*?3、時(shí)間過(guò)期 ?????*?4、spuriously ?????*?該操作放在?Unsafe?類(lèi)里沒(méi)有其它意義,它可以放在其它的任何地方 ?????*/ ????public?native?void?park(boolean?isAbsolute,?long?time);
阻塞和釋放當(dāng)前線程,java.util.concurrent 中的鎖就是通過(guò)這兩個(gè)方法實(shí)現(xiàn)線程阻塞和釋放的。
?
????/** ?????*獲取一段時(shí)間內(nèi),運(yùn)行的任務(wù)隊(duì)列分配到可用處理器的平均數(shù)(平常說(shuō)的?CPU?使用率) ?????* ?????*/ ????public?native?int?getLoadAverage(double[]?loadavg,?int?nelems);
統(tǒng)計(jì) CPU 負(fù)載。
?
????//?The?following?contain?CAS-based?Java?implementations?used?on ????//?platforms?not?supporting?native?instructions ????//下面的方法包含基于?CAS?的?Java?實(shí)現(xiàn),用于不支持本地指令的平臺(tái) ????/** ?????*?在給定的字段或數(shù)組元素的當(dāng)前值原子性的增加給定的值 ?????*?@param?o?字段/元素所在的對(duì)象/數(shù)組 ?????*?@param?offset?字段/元素的偏移 ?????*?@param?delta?需要增加的值 ?????*?@return?原值 ?????*?@since?1.8 ?????*/ ????public?final?int?getAndAddInt(Object?o,?long?offset,?int?delta)?{????????int?v;????????do?{ ????????????v?=?getIntVolatile(o,?offset); ????????}?while?(!compareAndSwapInt(o,?offset,?v,?v?+?delta));????????return?v; ????}????public?final?long?getAndAddLong(Object?o,?long?offset,?long?delta)?{????????long?v;????????do?{ ????????????v?=?getLongVolatile(o,?offset); ????????}?while?(!compareAndSwapLong(o,?offset,?v,?v?+?delta));????????return?v; ????}????/** ?????*?將給定的字段或數(shù)組元素的當(dāng)前值原子性的替換給定的值 ?????*?@param?o?字段/元素所在的對(duì)象/數(shù)組 ?????*?@param?offset?field/element?offset ?????*?@param?newValue?新值 ?????*?@return?原值 ?????*?@since?1.8 ?????*/ ????public?final?int?getAndSetInt(Object?o,?long?offset,?int?newValue)?{????????int?v;????????do?{ ????????????v?=?getIntVolatile(o,?offset); ????????}?while?(!compareAndSwapInt(o,?offset,?v,?newValue));????????return?v; ????}????public?final?long?getAndSetLong(Object?o,?long?offset,?long?newValue)?{????????long?v;????????do?{ ????????????v?=?getLongVolatile(o,?offset); ????????}?while?(!compareAndSwapLong(o,?offset,?v,?newValue));????????return?v; ????}????public?final?Object?getAndSetObject(Object?o,?long?offset,?Object?newValue)?{ ????????Object?v;????????do?{ ????????????v?=?getObjectVolatile(o,?offset); ????????}?while?(!compareAndSwapObject(o,?offset,?v,?newValue));????????return?v; ????}
基于 CAS 的一些原子操作實(shí)現(xiàn),也是比較常用的方法。
?
????//確保該欄桿前的讀操作不會(huì)和欄桿后的讀寫(xiě)操作發(fā)生重排序 ????public?native?void?loadFence();????//確保該欄桿前的寫(xiě)操作不會(huì)和欄桿后的讀寫(xiě)操作發(fā)生重排序 ????public?native?void?storeFence();????//確保該欄桿前的讀寫(xiě)操作不會(huì)和欄桿后的讀寫(xiě)操作發(fā)生重排序 ????public?native?void?fullFence();????//拋出非法訪問(wèn)錯(cuò)誤,僅用于VM內(nèi)部 ????private?static?void?throwIllegalAccessError()?{????????throw?new?IllegalAccessError(); ????}
這是實(shí)現(xiàn)內(nèi)存屏障的幾個(gè)方法,類(lèi)似于 volatile 的語(yǔ)意,保證內(nèi)存可見(jiàn)性和禁止重排序。
網(wǎng)站欄目:JDKUnsafe源碼完全注釋
標(biāo)題路徑:http://chinadenli.net/article12/ieihdc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、企業(yè)網(wǎng)站制作、網(wǎng)頁(yè)設(shè)計(jì)公司、網(wǎng)站制作、全網(wǎng)營(yíng)銷(xiāo)推廣、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)