實(shí)施例一般涉及用于在編程語言內(nèi)支持和/或利用改進(jìn)的存儲器訪問的技術(shù)。
背景技術(shù):
在多處理器系統(tǒng)中,處理器常常具有一個(gè)或多個(gè)存儲器高速緩存層,該一個(gè)或多個(gè)存儲器高速緩存層通過加速對數(shù)據(jù)的訪問和減少共享存儲器總線上的業(yè)務(wù)二者來提高性能。然而,雖然存儲器高速緩存可以大大提高性能,但是它們也提出了新的挑戰(zhàn)。例如,檢查同一存儲器位置的兩個(gè)處理器可能接收到不同的結(jié)果,因?yàn)橐粋€(gè)處理器可能使用陳舊的(stale)高速緩存值,然而另一個(gè)處理器可以從主存儲器拉取更新的值。此外,許多編譯器和計(jì)算機(jī)架構(gòu)重寫代碼,以優(yōu)化執(zhí)行。例如,處理器可以重寫或重新排序代碼,以利用存儲在其高速緩存中的當(dāng)前數(shù)據(jù)。然而,這些優(yōu)化中的許多優(yōu)化僅針對單個(gè)處理器/線程正在執(zhí)行程序的情況確保一致的程序語義。因此,在多處理器/多線程環(huán)境中,重新排序可能導(dǎo)致非預(yù)期的行為和不一致的程序狀態(tài)。例如,假如直到指令的原始程序索引才依賴變量,則計(jì)算機(jī)架構(gòu)可能在最方便的時(shí)候提前執(zhí)行加載/存儲。然而,在多個(gè)線程或多個(gè)處理器的情況下,提前執(zhí)行由其它線程依賴的操作可能導(dǎo)致否則將不可能遇到的狀態(tài)。
附圖說明
在附圖的圖中通過示例的方式而不是通過限制的方式示出本發(fā)明,在附圖中相似的附圖標(biāo)記指代相似的元件并且其中:
圖1是示出根據(jù)各種實(shí)施例的、在其中可以實(shí)現(xiàn)本文描述的某些技術(shù)的示例計(jì)算架構(gòu)的邏輯框圖。
圖2是示出根據(jù)實(shí)施例的示例類文件的框圖。
圖3是示出根據(jù)實(shí)施例的用于虛擬機(jī)運(yùn)行時(shí)環(huán)境的示例結(jié)構(gòu)的框圖。
圖4是示出根據(jù)實(shí)施例的用于虛擬機(jī)棧上的幀的示例結(jié)構(gòu)的框圖。
圖5示出了根據(jù)實(shí)施例的、以框圖形式的、用于使用VarHandle執(zhí)行原子操作的示例過程。
圖6是示出適合于實(shí)現(xiàn)本文所描述的方法和特征的計(jì)算機(jī)系統(tǒng)的一個(gè)實(shí)施例的框圖。
具體實(shí)施方式
在以下描述中,為了解釋的目的,闡述了許多具體細(xì)節(jié),以便提供對本發(fā)明的透徹理解。然而,將明顯的是,可以在沒有這些具體細(xì)節(jié)的情況下實(shí)踐本發(fā)明。在其它實(shí)例中,以框圖形式示出了公知的結(jié)構(gòu)和設(shè)備,以避免不必要地使本發(fā)明模糊。
本文根據(jù)以下提綱來描述實(shí)施例:
1.0總體概述
1.1受防護(hù)的操作
1.2原子操作
1.3暴露受約束的操作
2.0示例操作架構(gòu)
2.1示例類文件結(jié)構(gòu)
2.2示例虛擬機(jī)架構(gòu)
2.3加載、鏈接和初始化
3.0VarHandle
3.1示例受約束的操作
3.2示例接口
3.3VarHandle過程流字段示例
3.4VarHandle過程流數(shù)組示例
3.5優(yōu)化
3.6放寬關(guān)于多態(tài)簽名方法的返回類型
3.7通用多態(tài)簽名
3.8VarHandle和MethodHandle之間的代碼的合并
3.9存儲器圍欄
3.10VarHandle功能的擴(kuò)展
4.0硬件概述
5.0擴(kuò)展和可替代物
6.0附加公開
7.0第二附加公開
1.0.總體概述
本文描述了用于通過使用支持各種不同存儲器訪問模式的“句柄”來提供對存儲器位置的安全和高效的受約束訪問的技術(shù)。
1.1受防護(hù)的(fenced)操作
存儲器模型定義了用于知道何時(shí)由其它處理器對存儲器的寫入對當(dāng)前處理器可見以及由當(dāng)前處理器的寫入對其它處理器可見的條件。例如,一些處理器展現(xiàn)出強(qiáng)的存儲器模型,其中所有處理器對于任何給定的存儲器位置都看到完全相同的值。其它處理器展現(xiàn)出較弱的存儲器模型,其中被稱為存儲器“屏障”或“圍欄(fence)”的特殊指令沖刷(flush)本地處理器高速緩存或使本地處理器高速緩存無效,以便看到由其它處理器做出的寫入或者使處理器的寫入對其它處理器可見。
上面提到的存儲器屏障的效果是,使得在圍欄之前的操作的存儲器效果在圍欄之后的操作之前可見。因此,圍欄可以被看作防止操作被跨圍欄重新排序。在一些情況下,計(jì)算架構(gòu)提供對一般圍欄的支持,該一般圍欄防止在圍欄之前的任何加載/存儲操作與在圍欄之后的另一加載/存儲操作的重新排序。然而,其它計(jì)算架構(gòu)提供對細(xì)粒度圍欄的支持,該細(xì)粒度的圍欄防止某些類型的操作的重新排序,諸如加載-加載圍欄防止在圍欄之前的加載指令與圍欄之后的其它加載指令的重新排序、加載-存儲圍欄防止在圍欄之前的加載操作與在圍欄之后的存儲操作的重新排序,等等。細(xì)粒度的圍欄通常比一般圍欄需要更少的開銷,從而允許開發(fā)者通過使用提供所需功能的最便宜的圍欄來優(yōu)化其程序的執(zhí)行。某些類型的存儲器訪問操作利用圍欄,以便提高程序一致性以及避免競爭條件(race condition)。這些操作此后將被稱為“受防護(hù)的操作”或“受防護(hù)的訪問”,其包括諸如放寬的訪問(relaxed access)、易失性訪問(volatile access)、懶惰的訪問(lazy access)等等之類的技術(shù)。
1.2原子操作
原子操作還可以被用來確保程序一致性。原子操作是像訪問的單個(gè)單元一樣被執(zhí)行的操作。例如,獲得和設(shè)置(get-and-set)原子操作替換變量的當(dāng)前值并且“同時(shí)”返回變量的舊值。在一些實(shí)例中,原子操作經(jīng)由自旋鎖(spinlock)和存儲器圍欄實(shí)現(xiàn)。然而,許多計(jì)算機(jī)架構(gòu)支持可以由底層硬件高效執(zhí)行的本機(jī)(native)原子操作,而不需要在軟件級經(jīng)由自旋鎖和存儲器圍欄來實(shí)現(xiàn)。例如,處理器可以被配置為在相同的總線操作中讀取存儲器位置并且寫入存儲器位置,從而防止其它處理器在原子操作的中間訪問那個(gè)存儲器位置。如本文所使用的,“受約束的”操作/功能可以指代“受防護(hù)的”操作/功能和“原子”操作/功能中的任一或二者,因?yàn)椤笆芊雷o(hù)的”和“原子”操作/功能都對如何訪問和/或操縱表示變量的存儲器位置應(yīng)用約束。
1.3暴露受約束的操作
在一些情況下,如何以高效和方便的方式向這樣的受約束的操作暴露接口可以是重大的挑戰(zhàn)。例如,受約束的操作可以經(jīng)由對與任意存儲器位置交互的“不安全”庫的訪問來執(zhí)行。然而,對于許多語言和虛擬機(jī)開發(fā)者來說,允許編碼者訪問任意存儲器地址可能破壞平臺否則希望做到的安全保證,從而導(dǎo)致該語言/虛擬機(jī)被設(shè)計(jì)為最小化或徹底防止其發(fā)生的非期望的行為,諸如分段錯(cuò)誤或者更糟。例如,“不安全”庫可以是針對特定處理器類型優(yōu)化的本機(jī)庫,但是可以不檢查“更高級”語義,諸如確保對數(shù)組位置的寫入在界限內(nèi)。由于在使用“不安全”庫時(shí)出現(xiàn)的復(fù)雜性,語言和虛擬機(jī)開發(fā)者常常將防止或最小化非預(yù)期行為的可能性的責(zé)任放在編碼者手中。
另一個(gè)潛在的解決方案是將對“不安全”庫的訪問限制為已知具有以安全和負(fù)責(zé)的方式使用“不安全”庫的專門知識的受信任的開發(fā)者。然而,將對受約束的操作的訪問限制為開發(fā)者的小子集阻礙了其他開發(fā)者創(chuàng)建健壯和高效的軟件產(chǎn)品的能力。
1.4VARHANDLE
在實(shí)施例中,VarHandle(“變量句柄”的縮寫)是對存儲器的可執(zhí)行引用,諸如對由(一個(gè)或多個(gè))(受管理或不受管理的)存儲器位置定義的存儲器、(一個(gè)或多個(gè))類的(一個(gè)或多個(gè))對象或(一個(gè)或多個(gè))實(shí)例化、(一個(gè)或多個(gè))對象的(一個(gè)或多個(gè))字段、(一個(gè)或多個(gè))類的(一個(gè)或多個(gè))靜態(tài)字段、或者(一個(gè)或多個(gè))數(shù)組的(一個(gè)或多個(gè))元素的可執(zhí)行引用。存儲在被引用的存儲器中的值可以被稱為(一個(gè)或多個(gè))變量,并且VarHandle提供開發(fā)者可以通過其訪問要對(一個(gè)或多個(gè))變量執(zhí)行的受約束的操作的安全接口。在實(shí)施例中,VarHandle經(jīng)由保持變量的接收者(諸如保持字段的對象實(shí)例或保持元素的數(shù)組)來訪問變量。在低級別,經(jīng)由對與底層計(jì)算機(jī)硬件架構(gòu)通信并且訪問任意存儲器位置的“不安全”方法的調(diào)用來執(zhí)行受約束的操作。在一些實(shí)施例中,VarHandle被實(shí)現(xiàn)為類,并且該類的實(shí)例化被稱為VarHandle實(shí)例或?qū)ο蟆?/p>
例如,在Java的背景下,受約束的操作可以通過對“sun.misc.Unsafe”的調(diào)用來執(zhí)行,該“sun.misc.Unsafe”實(shí)現(xiàn)用于對表示各種變量(諸如對象、整型(int)、浮點(diǎn)型(float)、長整型(long)、雙精度型(double)等)的任意存儲器位置執(zhí)行受約束的操作的方法。然而,在許多情況下,將這些“不安全”方法暴露給軟件開發(fā)者造成存儲器位置可能以虛擬機(jī)不能預(yù)期的方式被操縱的風(fēng)險(xiǎn)。這可能導(dǎo)致虛擬機(jī)的設(shè)計(jì)者努力減輕或阻止的運(yùn)行時(shí)錯(cuò)誤和/或崩潰,諸如分段錯(cuò)誤。在實(shí)施例中,VarHandle提供了一種在方便和易于使用的接口中向開發(fā)者暴露這些低級的受約束的操作的安全方式。此外,在一些實(shí)施例中,虛擬機(jī)被配置為優(yōu)化訪問和安全檢查的方式,以實(shí)現(xiàn)與暴露到“不安全”方法的直接接口幾乎相同的運(yùn)行時(shí)性能,但是沒有直接利用“不安全”方法的缺點(diǎn)。
因此,VarHandle是將大大有益于軟件社區(qū)的、以安全、方便和高效的方式向開發(fā)者暴露受約束的操作的機(jī)制。例如,“無阻塞算法”的領(lǐng)域是涉及使用受約束的訪問作為基本并發(fā)原語的高性能并發(fā)數(shù)據(jù)結(jié)構(gòu)的研究領(lǐng)域。因此,提供其中適當(dāng)熟練的開發(fā)者可以容易且安全地實(shí)現(xiàn)這樣的算法的環(huán)境將對該領(lǐng)域有顯著益處。
2.0示例操作架構(gòu)
圖1示出了本文描述的技術(shù)可以在其中實(shí)踐的、包括運(yùn)行時(shí)環(huán)境112的示例計(jì)算架構(gòu)100。本文描述的技術(shù)常常使用來自Java編程語言、Java虛擬機(jī)(“JVM”)和Java運(yùn)行時(shí)環(huán)境的術(shù)語和定義。然而,設(shè)想的是,所描述的技術(shù)可以與任何編程語言、虛擬機(jī)架構(gòu)或運(yùn)行時(shí)環(huán)境結(jié)合使用。因此,例如,諸如“方法”之類的以Java術(shù)語描述的術(shù)語可以與其它術(shù)語(諸如“函數(shù)”)互換。此外,術(shù)語“方法”還與術(shù)語“類方法”或“對象方法”同義。方法是通過名稱指代并且可以在使得方法的代碼被執(zhí)行的程序中的各個(gè)點(diǎn)處被調(diào)用(啟用)的代碼集合。
如圖1中所示,運(yùn)行時(shí)環(huán)境112包括虛擬機(jī)104。虛擬機(jī)104包括各種組件,諸如存儲器管理器105(其可以包括垃圾收集器)、用于檢查虛擬機(jī)104代碼的有效性的類文件驗(yàn)證器106、用于定位和構(gòu)建類的存儲器中(in-memory)表示的類加載器107、以及用于執(zhí)行虛擬機(jī)104代碼的解釋器108(其可以包括即時(shí)“JIT”編譯器)。在一些實(shí)施例中,解釋器108實(shí)現(xiàn)解釋器和JIT編譯器二者的方面。
在實(shí)施例中,計(jì)算架構(gòu)100包括源代碼文件101,源代碼文件101包含以特定編程語言(諸如Java、C、C++、C#、Ruby、Perl等)編寫的代碼。因此,源代碼文件101遵守用于相關(guān)聯(lián)語言的語法和/或語義規(guī)則的特定集合。例如,用Java編寫的代碼遵守Java語言規(guī)范。然而,由于規(guī)范隨時(shí)間被更新和修訂,因此源代碼文件101可以與指示源代碼文件101所遵守的規(guī)范的修訂的版本號相關(guān)聯(lián)。用來編寫源代碼文件101的確切的編程語言一般不是關(guān)鍵的。
在各種實(shí)施例中,編譯器102將根據(jù)依照程序員的方便的規(guī)范編寫的源代碼轉(zhuǎn)換為可由特定機(jī)器環(huán)境直接執(zhí)行的機(jī)器代碼或目標(biāo)代碼,或者可由能夠在各種特定機(jī)器環(huán)境之上運(yùn)行的虛擬機(jī)104執(zhí)行的中間表示,諸如字節(jié)碼。字節(jié)碼可由虛擬機(jī)104以比源代碼更直接和高效的方式執(zhí)行。將源代碼轉(zhuǎn)換為字節(jié)碼包括將來自語言的源代碼功能映射到利用諸如數(shù)據(jù)結(jié)構(gòu)之類的底層資源的虛擬機(jī)功能。通常,由程序員經(jīng)由源代碼簡單地呈現(xiàn)的功能被轉(zhuǎn)換為更直接地映射為在由虛擬機(jī)104執(zhí)行期間將引起的機(jī)器操作的較復(fù)雜的步驟。
在一些實(shí)施例中,解釋器108實(shí)現(xiàn)解釋器和JIT編譯器二者的方面。例如,Oracle的HotSpot解釋來自“字節(jié)碼”的代碼,而且定位被頻繁執(zhí)行(“熱”)的字節(jié)碼的部分并且將那些部分編譯成適合底層計(jì)算機(jī)硬件的處理器的高效機(jī)器代碼。運(yùn)行時(shí)環(huán)境120可以在諸如操作系統(tǒng)110之類的較低級軟件之上運(yùn)行,在一些實(shí)施例中,該較低級軟件通過一個(gè)或多個(gè)應(yīng)用編程接口(API)而被訪問。虛擬機(jī)104、API 109和操作系統(tǒng)110的組合被稱為執(zhí)行平臺111。
為了提供清楚的示例,源代碼文件101被示為要由執(zhí)行平臺111執(zhí)行的程序的“最高級”表示。然而,雖然計(jì)算架構(gòu)100將源代碼文件101描繪為“最高級”程序表示,但是在其它實(shí)施例中,源代碼文件101可以是經(jīng)由將不同語言的代碼文件處理成源代碼文件101的語言的“較高級”編譯器接收的中間表示。為了說明清楚的示例,以下公開假設(shè)源代碼文件101遵守基于類的面向?qū)ο蟮木幊陶Z言。然而,這不是利用本文所描述的特征的要求。
在實(shí)施例中,編譯器102接收源代碼文件101作為輸入,并且將源代碼文件101轉(zhuǎn)換為具有虛擬機(jī)104所期望的格式的類文件103。例如,在JVM的背景下,Java虛擬機(jī)規(guī)范的第4章定義了期望類文件103遵守的特定類文件格式。在一些實(shí)施例中,類文件103包含已經(jīng)從源代碼文件101轉(zhuǎn)換而來的字節(jié)碼。然而,在其它實(shí)施例中,類文件103還可以包含其它結(jié)構(gòu),諸如識別與各種結(jié)構(gòu)(類、字段、方法等)相關(guān)的常量值和/或元數(shù)據(jù)的表。
以下討論將假設(shè)類文件103中的每個(gè)類文件表示在源代碼文件101中定義的(或由編譯器102/虛擬機(jī)104動(dòng)態(tài)生成的)相應(yīng)的“類”。然而,上面提到的假設(shè)不是嚴(yán)格的要求并且將取決于虛擬機(jī)104的實(shí)現(xiàn)。因此,不管類文件103的確切格式如何,本文描述的技術(shù)仍然可以被執(zhí)行。在一些實(shí)施例中,類文件103被劃分為一個(gè)或多個(gè)“庫”或“包”,該一個(gè)或多個(gè)“庫”或“包”中的每個(gè)包括提供相關(guān)功能的類的集合。例如,庫可以包含實(shí)現(xiàn)輸入/輸出(I/O)操作、數(shù)學(xué)工具、密碼技術(shù)、圖形實(shí)用程序等的一個(gè)或多個(gè)類文件。此外,一些類(或那些類內(nèi)的字段/方法)可以包括訪問限制,該訪問限制將它們的使用限制在特定類/庫/包內(nèi)或者限制為具有適當(dāng)許可(permission)的類。
2.1示例類文件結(jié)構(gòu)
圖2示出了根據(jù)實(shí)施例的、以框圖形式的用于類文件200的示例結(jié)構(gòu)。為了提供清楚的示例,本公開的剩余部分假設(shè)計(jì)算架構(gòu)100的類文件103遵守本節(jié)中描述的示例類文件200的結(jié)構(gòu)。然而,在實(shí)際環(huán)境中,類文件200的結(jié)構(gòu)將依賴于虛擬機(jī)104的實(shí)現(xiàn)。此外,本文討論的一個(gè)或多個(gè)特征可以修改類文件200的結(jié)構(gòu),以便例如添加附加的結(jié)構(gòu)類型。因此,類文件200的確切結(jié)構(gòu)對于本文所描述的技術(shù)不是關(guān)鍵的。為了節(jié)2.1的目的,“類”或“該類”是指由類文件200表示的類。
在圖2中,類文件200包括常量表201、字段結(jié)構(gòu)208、類元數(shù)據(jù)204和方法結(jié)構(gòu)209。
在實(shí)施例中,常量表201是除其他功能之外還充當(dāng)類的符號表的數(shù)據(jù)結(jié)構(gòu)。例如,常量表201可以存儲與在源代碼文件101中使用的各種標(biāo)識符(諸如類型、范圍、內(nèi)容和/或位置)相關(guān)的數(shù)據(jù)。常量表201具有用于由編譯器102從源代碼文件101導(dǎo)出的值結(jié)構(gòu)202(表示類型整型、長整型、雙精度型、浮點(diǎn)型、字節(jié)型(byte)、字符串型(string)等的常量值)、類信息結(jié)構(gòu)203、名稱和類型信息結(jié)構(gòu)205、字段引用結(jié)構(gòu)206以及方法引用結(jié)構(gòu)207的條目。在實(shí)施例中,常量表201被實(shí)現(xiàn)為將索引i映射到結(jié)構(gòu)j的數(shù)組。然而,常量表201的確切實(shí)現(xiàn)不是關(guān)鍵的。
在一些實(shí)施例中,常量表201的條目包括索引其它常量表201條目的結(jié)構(gòu)。例如,用于表示字符串的值結(jié)構(gòu)202中的一個(gè)值結(jié)構(gòu)的條目可以保持將其“類型”識別為字符串的標(biāo)簽,以及對存儲表示該字符串的ASCII字符的字符型(char)、字節(jié)型或整型值的常量表201的一個(gè)或多個(gè)其它值結(jié)構(gòu)202的索引。
在實(shí)施例中,常量表201的字段引用結(jié)構(gòu)206保持對常量表201中的類信息結(jié)構(gòu)203中表示定義字段的類的一個(gè)類信息結(jié)構(gòu)的索引以及對常量表201中的名稱和類型信息結(jié)構(gòu)205中提供字段的名稱和描述符的一個(gè)名稱和類型信息結(jié)構(gòu)的索引。常量表201的方法引用結(jié)構(gòu)207保持對常量表201中的類信息結(jié)構(gòu)203中表示定義方法的類的一個(gè)類信息結(jié)構(gòu)的索引以及對常量表201中的名稱和類型信息結(jié)構(gòu)205中提供用于方法的名稱和描述符的一個(gè)名稱和類型信息結(jié)構(gòu)的索引。類信息結(jié)構(gòu)203保持對常量表201中的值結(jié)構(gòu)202中保持相關(guān)聯(lián)的類的名稱的一個(gè)值結(jié)構(gòu)的索引。名稱和類型信息結(jié)構(gòu)205保持對常量表201中的值結(jié)構(gòu)202中存儲字段/方法的名稱的一個(gè)值結(jié)構(gòu)的索引以及對常量表201中的值結(jié)構(gòu)202中存儲描述符的一個(gè)值結(jié)構(gòu)的索引。
在實(shí)施例中,類元數(shù)據(jù)204包括用于類的元數(shù)據(jù),諸如(一個(gè)或多個(gè))版本號、常量池中的條目數(shù)、字段數(shù)、方法數(shù)、訪問標(biāo)志(類是否是公有的、私有的、最終的、抽象的,等等)、對常量表201的類信息結(jié)構(gòu)203中識別該類的一個(gè)類信息結(jié)構(gòu)的索引、對常量表201的類信息結(jié)構(gòu)203中識別超類(如果有的話)的一個(gè)類信息結(jié)構(gòu)的索引,等等。
在實(shí)施例中,字段結(jié)構(gòu)208表示識別類的各個(gè)字段的一組結(jié)構(gòu)。字段結(jié)構(gòu)208為類的每個(gè)字段存儲用于該字段的訪問器標(biāo)志(字段是否是靜態(tài)的、公有的、私有的、最終的,等等)、對常量表201中的值結(jié)構(gòu)202中保持字段的名稱的一個(gè)值結(jié)構(gòu)的索引、以及對常量表201中的值結(jié)構(gòu)202中保持字段的描述符的一個(gè)值結(jié)構(gòu)的索引。
在實(shí)施例中,方法結(jié)構(gòu)209表示識別類的各種方法的一組結(jié)構(gòu)。方法結(jié)構(gòu)209為類的每個(gè)方法存儲用于該方法的訪問器標(biāo)志(例如,該方法是否是靜態(tài)的、公共的、私有的、同步的,等等)、對常量表201中的值結(jié)構(gòu)202中保持方法的名稱的一個(gè)值結(jié)構(gòu)的索引、對常量表201中的值結(jié)構(gòu)202中保持方法的描述符的一個(gè)值結(jié)構(gòu)的索引、以及與如源代碼文件101中定義的方法的主體對應(yīng)的虛擬機(jī)104指令。
在實(shí)施例中,描述符表示字段或方法的類型。例如,描述符可以被實(shí)現(xiàn)為遵守特定語法的字符串。雖然確切的語法并不關(guān)鍵,但是下文描述幾個(gè)示例。
在描述符表示字段的類型的示例中,描述符識別由該字段保持的數(shù)據(jù)的類型。在實(shí)施例中,字段可以保持基本類型、對象或數(shù)組。當(dāng)字段保持基本類型時(shí),描述符是識別該基本類型的字符串(例如,“B”=byte(字節(jié)型)、“C”=char(字符型)、“D”=double(雙精度型)、“F”=float(浮點(diǎn)型)、“I”=int(整型)、“J”=long int(長整型)等)。當(dāng)字段保持對象時(shí),描述符是識別該對象的類名稱的字符串(例如,“L ClassName”)。在這種情況下,“L”指示引用,因此“L ClassName”表示對類ClassName的對象的引用。當(dāng)字段是數(shù)組時(shí),描述符識別由該數(shù)組保持的類型。例如,“[B”指示字節(jié)型的數(shù)組,其中“[”指示數(shù)組,而“B”指示該數(shù)組保持字節(jié)型的基本類型。然而,由于數(shù)組可以嵌套,因此數(shù)組的描述符還可以指示嵌套。例如,“[[L ClassName”指示數(shù)組,在該數(shù)組中每個(gè)索引保持保持類ClassName的對象的數(shù)組。在一些實(shí)施例中,ClassName是完全限定的并且包括類的簡單名稱以及類的路徑名稱。例如,ClassName可以指示文件存儲在托管類文件200的包、庫或文件系統(tǒng)中的何處。
在方法的情況下,描述符識別方法的參數(shù)和方法的返回類型。例如,方法描述符可以遵循一般形式“({ParameterDescriptor})ReturnDescriptor”,其中{ParameterDescriptor}是表示參數(shù)的字段描述符的列表,而ReturnDescriptor是識別返回類型的字段描述符。例如,字符串“V”可以被用于表示void(空)返回類型。因此,在源代碼文件101中被定義為“Object m(int I,double d,Thread t){…}”的方法匹配描述符“(I D L Thread)L Object”。
在實(shí)施例中,方法結(jié)構(gòu)209中保持的虛擬機(jī)104指令包括引用常量表201的條目的操作。
使用Java作為示例,考慮以下類
在上面的示例中,Java方法add12and13在類A中定義、不帶參數(shù)并且返回整數(shù)。方法add12and13的主體調(diào)用采用常量整數(shù)值12和13作為參數(shù)的類B的靜態(tài)方法addTwo,并且返回結(jié)果。因此,在常量表201中,編譯器102除其它條目之外還包括對應(yīng)于對方法B.addTwo的調(diào)用的方法引用結(jié)構(gòu)。在Java中,對方法的調(diào)用向下編譯為JVM的字節(jié)碼中的invoke命令(在該情況中為invokestatic,因?yàn)閍ddTwo是類B的靜態(tài)方法)。向invoke命令提供對應(yīng)于識別定義addTwo的類“B”、addTwo的名稱“addTwo”以及addTwo的描述符“(I I)I”的方法引用結(jié)構(gòu)的對常量表201的索引。例如,假設(shè)上面提到的方法引用被存儲在索引4處,則字節(jié)碼指令可以看起來是“invokestatic#4”。
由于常量表201用攜帶識別信息的結(jié)構(gòu)以符號形式指代類、方法和字段,而不是用對存儲器位置的直接引用來指代類、方法和字段,因此常量表201的條目被稱為“符號引用”。符號引用被用于類文件103的一個(gè)原因是因?yàn)樵谝恍?shí)施例中編譯器102不知道類一旦被加載到運(yùn)行時(shí)環(huán)境112中將怎樣被存儲以及將被存儲在何處。如將在節(jié)2.3中描述的,在所引用的類(和相關(guān)聯(lián)的結(jié)構(gòu))已經(jīng)被加載到運(yùn)行時(shí)環(huán)境中并且已經(jīng)被分配了具體的存儲器位置之后,最終,符號引用的運(yùn)行時(shí)表示由虛擬機(jī)104解析為實(shí)際的存儲器地址。
2.2示例虛擬機(jī)架構(gòu)
圖3示出了根據(jù)實(shí)施例的以框圖形式的示例虛擬機(jī)存儲器布局300。為了提供清楚的示例,剩余的討論將假設(shè)虛擬機(jī)104遵守圖3中所描繪的虛擬機(jī)存儲器布局300。此外,雖然虛擬機(jī)存儲器布局300的組件可以被稱為存儲器“區(qū)域”,但是不要求存儲器區(qū)域是相鄰的。
在圖3示出的示例中,虛擬機(jī)存儲器布局300被劃分為共享區(qū)域301和線程區(qū)域307。
共享區(qū)域301表示存儲器中存儲在虛擬機(jī)104上執(zhí)行的各種線程之間共享的結(jié)構(gòu)的區(qū)域。共享區(qū)域301包括堆302和按類的(per-class)區(qū)域303。在實(shí)施例中,堆302表示從其分配用于類實(shí)例和數(shù)組的存儲器的運(yùn)行時(shí)數(shù)據(jù)區(qū)域。在實(shí)施例中,按類的區(qū)域303表示存儲與單獨(dú)的類有關(guān)的數(shù)據(jù)的存儲器區(qū)域。在實(shí)施例中,對于每個(gè)加載的類,按類的區(qū)域303包括表示來自類的常量表201的數(shù)據(jù)的運(yùn)行時(shí)常量池304、字段和方法數(shù)據(jù)306(例如,為了保持類的靜態(tài)字段)以及表示用于類的方法的虛擬機(jī)104指令的方法代碼305。
線程區(qū)域307表示其中存儲特定于單獨(dú)的線程的結(jié)構(gòu)的存儲器區(qū)域。在圖3中,線程區(qū)域307包括表示由不同線程利用的每線程結(jié)構(gòu)的線程結(jié)構(gòu)308和線程結(jié)構(gòu)311。為了提供清楚的示例,圖3中所描繪的線程區(qū)域307假設(shè)兩個(gè)線程正在虛擬機(jī)104上執(zhí)行。然而,在實(shí)際環(huán)境中,虛擬機(jī)104可以執(zhí)行任何任意數(shù)量的線程,其中相應(yīng)地縮放線程結(jié)構(gòu)的數(shù)量。
在實(shí)施例中,線程結(jié)構(gòu)308包括程序計(jì)數(shù)器309和虛擬機(jī)棧310。類似地,線程結(jié)構(gòu)311包括程序計(jì)數(shù)器312和虛擬機(jī)棧313。在實(shí)施例中,程序計(jì)數(shù)器309和程序計(jì)數(shù)器312存儲由它們各自的線程執(zhí)行的虛擬機(jī)指令的當(dāng)前地址。因此,當(dāng)線程逐句通過(step through)指令時(shí),程序計(jì)數(shù)器被更新以維護(hù)對當(dāng)前指令的索引。在實(shí)施例中,虛擬機(jī)棧310和虛擬機(jī)棧313各自存儲保持局部變量和部分結(jié)果的、用于它們各自的線程的幀,并且還被用于方法啟用和返回。
在實(shí)施例中,幀是被用來存儲數(shù)據(jù)和部分結(jié)果、返回用于方法的值以及執(zhí)行動(dòng)態(tài)鏈接的數(shù)據(jù)結(jié)構(gòu)。每次啟用方法時(shí)都創(chuàng)建新的幀。當(dāng)使得幀被生成的方法完成時(shí),幀被銷毀。因此,當(dāng)線程執(zhí)行方法啟用(invocation)時(shí),虛擬機(jī)104生成新幀并將該幀推送到與該線程相關(guān)聯(lián)的虛擬機(jī)棧上。當(dāng)方法啟用完成時(shí),虛擬機(jī)104將方法啟用的結(jié)果傳遞回前一幀,并且使當(dāng)前幀出棧。在實(shí)施例中,對于給定的線程,在任何時(shí)間點(diǎn)處有一個(gè)幀是活動(dòng)的。這個(gè)活動(dòng)幀被稱為當(dāng)前幀,使得當(dāng)前幀被生成的方法被稱為當(dāng)前方法,并且當(dāng)前方法所屬的類被稱為當(dāng)前類。
圖4示出了根據(jù)實(shí)施例的以框圖形式的示例幀400。為了提供清楚的示例,剩余的討論將假設(shè)虛擬機(jī)棧310和虛擬機(jī)棧313的幀遵守幀400的結(jié)構(gòu)。
在實(shí)施例中,幀400包括局部變量401、操作數(shù)棧402和運(yùn)行時(shí)常量池引用表403。
在實(shí)施例中,局部變量401被表示為各自保持諸如布爾型(Boolean)、字節(jié)型、字符型、短整型、整型、浮點(diǎn)型、引用型等之類的值的變量的數(shù)組。此外,諸如長整型或雙精度型之類的一些值類型可以由數(shù)組中的多于一個(gè)條目表示。局部變量401被用來在方法啟用時(shí)傳遞參數(shù)以及存儲部分結(jié)果。例如,當(dāng)響應(yīng)于啟用方法而生成幀400時(shí),參數(shù)可以被存儲在局部變量401內(nèi)預(yù)定義的位置中,諸如對應(yīng)于啟用中的第一個(gè)至第N個(gè)參數(shù)的索引1-N。
在實(shí)施例中,當(dāng)幀400由虛擬機(jī)104創(chuàng)建時(shí),操作數(shù)棧402默認(rèn)是空的。然后,虛擬機(jī)104提供來自當(dāng)前方法的方法代碼305的指令,以將來自局部變量501的常量或值加載到操作數(shù)棧502上。其它指令從操作數(shù)棧402取出操作數(shù)、對它們進(jìn)行操作并且將結(jié)果推回到操作數(shù)棧402上。此外,操作數(shù)棧402被用來準(zhǔn)備要被傳遞到方法的參數(shù)以及接收方法結(jié)果。例如,在發(fā)出對方法的啟用之前,被啟用的方法的參數(shù)可以被推送到操作數(shù)棧402上。然后,虛擬機(jī)104生成用于方法啟用的新幀,其中前一幀的操作數(shù)棧402上的操作數(shù)出棧并且被加載到新幀的局部變量401中。當(dāng)被啟用的方法終止時(shí),新幀從虛擬機(jī)棧出棧并且返回值被推送到前一幀的操作數(shù)棧402上。
在實(shí)施例中,運(yùn)行時(shí)常量池引用表403包含對當(dāng)前類的運(yùn)行時(shí)常量池304的引用。運(yùn)行時(shí)常量池引用表403被用來支持解析(resolution)。解析是這樣的過程:通過該過程,常量池304中的符號引用被翻譯成具體的存儲器地址,從而按照需要加載類以解析尚未定義的符號并且將變量訪問翻譯成與這些變量的運(yùn)行時(shí)位置相關(guān)聯(lián)的存儲結(jié)構(gòu)中的適當(dāng)偏移。
2.3加載、鏈接和初始化
在實(shí)施例中,虛擬機(jī)104動(dòng)態(tài)地加載、鏈接和初始化類。加載是尋找具有特定名稱的類并且在運(yùn)行時(shí)環(huán)境112的存儲器內(nèi)創(chuàng)建來自該類的相關(guān)聯(lián)的類文件200的表示的過程。例如,在虛擬機(jī)存儲器布局300的按類的區(qū)域303內(nèi)創(chuàng)建用于類的運(yùn)行時(shí)常量池304、方法代碼305以及字段和方法數(shù)據(jù)306。鏈接是取出類的存儲器中表示并且將其與虛擬機(jī)104的運(yùn)行時(shí)狀態(tài)組合以使得類的方法可以被執(zhí)行的過程。初始化是執(zhí)行類構(gòu)造函數(shù)(constructor)以設(shè)置類的字段和方法數(shù)據(jù)306的起始狀態(tài)和/或?yàn)楸怀跏蓟念愒诙?02上創(chuàng)建類實(shí)例的過程。
以下是可以由虛擬機(jī)104實(shí)現(xiàn)的加載、鏈接和初始化技術(shù)的示例。然而,在許多實(shí)施例中,步驟可以是交錯(cuò),以使得初始類被加載,然后在鏈接期間第二個(gè)類被加載以解析在第一個(gè)類中發(fā)現(xiàn)的符號引用,這又導(dǎo)致第三個(gè)類被加載,等等。因此,通過加載、鏈接和初始化的階段的進(jìn)程可以根據(jù)類而不同。此外,一些實(shí)施例可以延遲(“懶惰地”執(zhí)行)加載、鏈接和初始化過程的一個(gè)或多個(gè)功能,直到類被實(shí)際需要。例如,方法引用的解析可以被延遲直到啟用被引用的方法的虛擬機(jī)104指令被執(zhí)行。因此,對于每個(gè)類何時(shí)執(zhí)行步驟的確切時(shí)機(jī)在實(shí)施方式之間可以差別很大。
為了開始加載過程,虛擬機(jī)104通過啟用加載初始類的類加載器107來啟動(dòng)。用于指定初始類的技術(shù)將根據(jù)實(shí)施例而不同。例如,一種技術(shù)可以使虛擬機(jī)104在啟動(dòng)時(shí)接受指定初始類的命令行變元(argument)。
為了加載類,類加載器107解析與該類對應(yīng)的類文件200,并且確定類文件200是否為形式良好的(滿足虛擬機(jī)104的語法期待)。如果不是,則類加載器107生成錯(cuò)誤。例如,在Java中,可能以異常的形式生成錯(cuò)誤,該異常被拋給異常處理器以供處理。否則,類加載器107通過在按類的區(qū)域303內(nèi)分配用于類的運(yùn)行時(shí)常量池304、方法代碼305以及字段和方法數(shù)據(jù)306來生成類的存儲器中表示。
在一些實(shí)施例中,當(dāng)類加載器107加載類時(shí),類加載器107還遞歸地加載被加載類的超類。例如,虛擬機(jī)104可以確保在繼續(xù)進(jìn)行對特定類的加載、鏈接和初始化過程之前該特定類的超類被加載、鏈接和/或初始化。
在鏈接期間,虛擬機(jī)104驗(yàn)證類、準(zhǔn)備類并且執(zhí)行在類的運(yùn)行時(shí)常量池304中定義的符號引用的解析。
為了驗(yàn)證類,虛擬機(jī)104檢查類的存儲器中表示在結(jié)構(gòu)上是否是正確的。例如,虛擬機(jī)104可以檢查除了泛型(generic)類對象(Object)之外的每個(gè)類具有超類、檢查最終(final)類沒有子類以及最終方法沒有被重寫(override)、檢查常量池條目是否彼此一致,檢查當(dāng)前類是否具有對常量池304中引用的類/字段/結(jié)構(gòu)的正確訪問許可、檢查方法的虛擬機(jī)104代碼不會引起非預(yù)期行為(例如,確保跳轉(zhuǎn)指令不會使虛擬機(jī)104超出方法的末尾),等等。在驗(yàn)證期間執(zhí)行的確切檢查取決于虛擬機(jī)104的實(shí)現(xiàn)。在一些情況下,驗(yàn)證可以導(dǎo)致附加的類被加載,但是在繼續(xù)進(jìn)行之前不一定要求那些類也被鏈接。例如,假設(shè)類A包含對類B的靜態(tài)字段的引用。在驗(yàn)證期間,虛擬機(jī)104可以檢查類B,以確保所引用的靜態(tài)字段實(shí)際存在,這可能導(dǎo)致類B的加載,但不一定導(dǎo)致類B的鏈接或初始化。然而,在一些實(shí)施例中,某些驗(yàn)證檢查可以被延遲直到較晚的階段,諸如在符號引用的解析期間被檢查。例如,一些實(shí)施例可以推遲檢查對于符號引用的訪問許可直到這些引用被解析。
為了準(zhǔn)備類,虛擬機(jī)104將位于該類的字段和方法數(shù)據(jù)306內(nèi)的靜態(tài)字段初始化為默認(rèn)值。在一些情況下,將靜態(tài)字段設(shè)置為默認(rèn)值可以與運(yùn)行類的構(gòu)造函數(shù)不同。例如,驗(yàn)證過程可以在初始化期間將靜態(tài)字段清零或?qū)㈧o態(tài)字段設(shè)置為構(gòu)造函數(shù)期望這些字段具有的值。
在解析期間,虛擬機(jī)104從包括在類的運(yùn)行時(shí)常量池304中的符號引用動(dòng)態(tài)地確定具體的存儲器地址。為了解析符號引用,虛擬機(jī)104利用類加載器107加載符號引用中識別出的類(如果還未加載的話)。一旦被加載,則虛擬機(jī)104知道所引用的類和它的字段/方法的按類的區(qū)域303內(nèi)的存儲器位置。然后,虛擬機(jī)104用對所引用的類、字段或方法的具體存儲器位置的引用來替換符號引用。在實(shí)施例中,虛擬機(jī)104高速緩存解析,以便在當(dāng)虛擬機(jī)104處理另一個(gè)類時(shí)遇到相同的類/名稱/描述符的情況下重用。例如,在一些情況下,類A和類B可以啟用類C的同一方法。因此,當(dāng)對類A執(zhí)行解析時(shí),該結(jié)果可以被高速緩存并且在類B中的相同符號引用的解析期間被重用,以減少開銷。
在一些實(shí)施例中,在鏈接期間解析符號引用的步驟是可選的。例如,實(shí)施例可以以“懶惰的”方式執(zhí)行符號解析,從而延遲解析的步驟直到需要所引用的類/方法/字段的虛擬機(jī)104指令被執(zhí)行。
在初始化期間,虛擬機(jī)104執(zhí)行類的構(gòu)造函數(shù)以設(shè)置該類的起始狀態(tài)。例如,初始化可以初始化該類的字段和方法數(shù)據(jù)306,以及在由構(gòu)造函數(shù)創(chuàng)建的堆302上生成/初始化任何類實(shí)例。例如,用于類的類文件200可以指定特定方法是用于設(shè)置起始狀態(tài)的構(gòu)造函數(shù)。因此,在初始化期間,虛擬機(jī)104執(zhí)行該構(gòu)造函數(shù)的指令。
在一些實(shí)施例中,虛擬機(jī)104通過最初檢查字段/方法在所引用的類中是否被定義來執(zhí)行對字段和方法引用的解析。否則,虛擬機(jī)104針對所引用的方法/字段遞歸搜索所引用的類的超類直到字段/方法被定位或到達(dá)最高級的超類,在到達(dá)最高級的超類的情況下生成錯(cuò)誤。
3.0VARHANDLE
在實(shí)施例中,VarHandle是對存儲器的可執(zhí)行引用,諸如對由(一個(gè)或多個(gè))(受管理或不受管理的)存儲器位置定義的存儲器、(一個(gè)或多個(gè))類的(一個(gè)或多個(gè))對象或(一個(gè)或多個(gè))實(shí)例化、(一個(gè)或多個(gè))對象的(一個(gè)或多個(gè))字段、(一個(gè)或多個(gè))類的(一個(gè)或多個(gè))靜態(tài)字段或者(一個(gè)或多個(gè))數(shù)組的(一個(gè)或多個(gè))元素的可執(zhí)行引用。在一些實(shí)施例中,VarHandle被實(shí)現(xiàn)為可以被實(shí)例化以創(chuàng)建VarHandle實(shí)例/對象的類。在實(shí)施例中,VarHandle經(jīng)由保持變量的接收者(諸如保持字段的對象實(shí)例或保持元素的數(shù)組)來訪問變量。在低級別,經(jīng)由對與底層計(jì)算機(jī)硬件架構(gòu)通信并且訪問任意存儲器位置的低級內(nèi)部函數(shù)(intrinsic)的調(diào)用來執(zhí)行受約束的操作。例如,在Java的背景下,可以通過對“sun.misc.Unsafe”的調(diào)用來執(zhí)行受約束的操作,該“sun.misc.Unsafe”實(shí)現(xiàn)用于對表示各種變量(諸如對象、整型、浮點(diǎn)型、長整型、雙精度型等)的任意存儲器位置執(zhí)行受約束的操作的方法。然而,在許多情況下,向軟件開發(fā)者暴露這些“不安全”方法造成存儲器位置可能以虛擬機(jī)104不能預(yù)期的方式被操縱的風(fēng)險(xiǎn)。這可能導(dǎo)致虛擬機(jī)104的設(shè)計(jì)者努力減輕或阻止的運(yùn)行時(shí)錯(cuò)誤和/或崩潰,諸如分段錯(cuò)誤。
一種先前的解決方案是將對“不安全”方法的訪問限制為已知具有以安全和負(fù)責(zé)的方式使用“不安全”庫的專門知識的受信任的開發(fā)者。然而,將對受約束的操作的訪問限制為開發(fā)者的小子集阻礙了其他開發(fā)者創(chuàng)建健壯和高效的軟件產(chǎn)品的能力。在實(shí)施例中,VarHandle提供了一種在方便和易于使用的接口中向開發(fā)者暴露這些低級受約束的操作的安全方式。此外,在一些實(shí)施例中,虛擬機(jī)104被配置為優(yōu)化訪問和安全檢查的方式,以實(shí)現(xiàn)與暴露到“不安全”方法的直接接口幾乎相同的運(yùn)行時(shí)性能,但是沒有允許對“不安全”方法的直接訪問的固有缺點(diǎn)。
3.1示例受約束的操作
本文描述的技術(shù)適用于幾乎任何類型的受約束的操作。然而,為了提供清楚的示例,參考VarHandle描述的操作將包括放寬的獲得(relaxed-get)和放寬的設(shè)置(relaxed-set)、易失性獲得和易失性設(shè)置,獲取獲得(acquire-get)和釋放設(shè)置(release-set)(懶惰的獲得/設(shè)置),以及比較和設(shè)置(compare-and-set)和獲得和設(shè)置(get-and-set)。然而,不同的實(shí)施例可以實(shí)現(xiàn)受約束操作的不同集合。例如,其它實(shí)施例還可以支持原子的加、減或其它原子算術(shù)運(yùn)算。
在實(shí)施例中,放寬的獲得和放寬的設(shè)置執(zhí)行放寬的受防護(hù)操作。在放寬的受防護(hù)操作中,僅在特定線程的執(zhí)行內(nèi)保證排序。因此,如果一個(gè)線程執(zhí)行存儲,則不能保證另一個(gè)線程將以相同的順序看到存儲。例如,如果線程A執(zhí)行對變量a的存儲1,然后執(zhí)行對變量b的存儲2,則有可能對變量b的存儲在對變量a的存儲之前對另一線程B變得可見。
在實(shí)施例中,易失性獲得和易失性設(shè)置執(zhí)行易失性受防護(hù)操作。在易失性受防護(hù)操作中,在易失性存儲和易失性加載之間實(shí)施順序排序。因此,易失性受防護(hù)操作用直接對主存儲器執(zhí)行的讀取和寫入來繞過本地線程高速緩存。
在實(shí)施例中,獲取獲得和釋放設(shè)置執(zhí)行懶惰的受防護(hù)操作。在懶惰的受防護(hù)操作中,在釋放設(shè)置和獲取獲得之間執(zhí)行同步,以確保在執(zhí)行期間的特定點(diǎn)處發(fā)生順序(happens-before)關(guān)系在線程之間可以繼續(xù)保留。例如,如果線程A使用釋放設(shè)置來執(zhí)行對變量a的存儲1,然后使用釋放設(shè)置來執(zhí)行對變量b的存儲2,則變量a和變量b的存儲之間的發(fā)生順序關(guān)系相對于后續(xù)的獲取獲得被維持。因此,如果線程B在對b的存儲變得可見之后使用獲取獲得來讀取變量b,則將保證對a的存儲會在線程B的上下文中預(yù)先發(fā)生。因此,一旦對b的存儲作為獲取獲得的結(jié)果而可見,則對a的存儲也對線程B可見。
在實(shí)施例中,比較和設(shè)置是將存儲器位置的內(nèi)容與給定值進(jìn)行比較并且僅當(dāng)比較為真(true)時(shí)將內(nèi)容修改為新值的原子操作。在實(shí)施例中,獲得和設(shè)置是將值寫入存儲器位置并且返回存儲器位置的舊值的原子操作。
在一些實(shí)施例中,VarHandle還支持對變量的“正?!被颉盁o約束”訪問,該“正常”或“無約束”訪問不提供關(guān)于對存儲器中的變量的存儲/加載的原子性或重新排序的保證。因此,如本文所描述的實(shí)施例適用于寬范圍的排序和原子性約束,包括與“正?!贝鎯ζ髟L問相關(guān)聯(lián)的“空(null)”約束。因此,在一些實(shí)施例中,“正?!被颉盁o約束”訪問模式是使用VarHandle可用的若干選項(xiàng)中的一個(gè)選項(xiàng),其中每個(gè)選項(xiàng)對存儲器訪問施加不同級別的約束。
3.2示例接口
如上面所提到的,VarHandle引用保持變量的“接收者”,諸如保持字段的對象實(shí)例或保持元素的數(shù)組?!敖邮照摺钡念愋秃陀伞敖邮照摺北3值摹白兞俊钡念愋褪翘囟ㄓ趯?shí)現(xiàn)的。本文所描述的技術(shù)適用于幾乎任何類型的“接收者”和“變量”。然而,以下表示可以經(jīng)由VarHandle訪問的“接收者”和“變量”的示例。
在實(shí)施例中,VarHandle保持對以下的一個(gè)或多個(gè)的引用:(1)靜態(tài)字段,其中接收者是保存靜態(tài)字段的類,(2)實(shí)例字段,其中接收者是保持字段的類的實(shí)例,或(3)數(shù)組元素,其中接收者是在數(shù)組中的定義的索引處保持該元素的數(shù)組。因此,每個(gè)接收者與特定類型的變量訪問相關(guān)聯(lián),諸如(1)參考靜態(tài)訪問、(2)參考實(shí)例訪問,及(3)參考數(shù)組訪問。
然而,在一些實(shí)施例中,VarHandle表示參考虛擬機(jī)104外部的堆外(off-heap)或不受管理的存儲器位置的接收者。例如,可以替代地使用基于庫的接收者類型,而不是基于語言的接收者類型,諸如保持字段的類/保持元素的數(shù)組。在這種情況下,接收者類型可以在具有作為接收者接受該類的實(shí)例的對應(yīng)VarHandle實(shí)現(xiàn)的庫中被定義。例如,一個(gè)這樣的表示可以是保持到堆外/直接/不受管理的存儲器的基地址(base address)以及界限的接收者類型,其中存儲器訪問被約束為[基地址,基地址+界限]。然后,VarHandle將存儲與該接收者類型相關(guān)的數(shù)據(jù)并且相應(yīng)地使用基地址/界限來訪問存儲器。
在實(shí)施例中,由“接收者”保持的變量包括以下的一個(gè)或多個(gè):“Object”引用、靜態(tài)類型的“Object”引用(“Object”的子類型)、“整型”或“長整型”。然而,其它實(shí)施例可以具有可以保持許多其它類型的原語(primitive)變量的“接收者”,諸如浮點(diǎn)型、雙精度型、字符型等等。另外,其它實(shí)施例可以提供對于保持“類似原語”變量(諸如字符串)的“接收者”的支持。此外,在其它實(shí)施例中,由“接收者”保持的變量可以表示在堆302外部或者甚至在運(yùn)行時(shí)環(huán)境112外部的存儲器區(qū)域。例如,VarHandle可以表示對不同虛擬機(jī)/運(yùn)行時(shí)環(huán)境的存儲器區(qū)域中的結(jié)構(gòu)的引用。為了清晰,斜體的Object指的是充當(dāng)所有其它類的超類的泛型對象結(jié)構(gòu),正如在Java編程語言中那樣。其它對象類型被稱為“靜態(tài)類型的對象”并且是Object的子類。
在一些實(shí)施例中,VarHandle對保持值類型的接收者執(zhí)行受約束的操作,如在2014年5月13日提交的美國臨時(shí)申請61/992,753中所描述的那樣,在此出于所有目的通過引用并入該申請,如同本文完全闡述了該申請一樣。
在示例實(shí)施例中,假設(shè)存在三種類型的接收者和四種類型的變量,對于每個(gè)受約束的操作,實(shí)現(xiàn)的數(shù)量為12(3×4)。例如,處理對Object的靜態(tài)字段、靜態(tài)類型對象的數(shù)組、整型的實(shí)例字段以及各種組合等的受約束訪問的實(shí)現(xiàn)。接收者類型和變量類型的組合將被稱為“訪問類型”。此外,在示例實(shí)施例中存在9(3×3)個(gè)不同的接口形狀,因?yàn)镺bject引用和靜態(tài)類型對象引用可以共享公共的接口形狀(可以使用泛型Object類型來表示二者),但是具有不同的實(shí)現(xiàn)。例如,后者可以執(zhí)行顯式的類型轉(zhuǎn)換,而對于前者來說類型轉(zhuǎn)換將是多余的。然而,實(shí)現(xiàn)和/或接口形狀的數(shù)量取決于由給定實(shí)施例支持的不同訪問類型的數(shù)量。此外,假設(shè)存在八種不同類型的支持的受約束操作,則示例實(shí)施例將擁有96(8×12)種不同的受約束操作實(shí)現(xiàn)。例如,其中接收者是靜態(tài)字段并且變量是靜態(tài)類型對象的放寬的獲得的實(shí)現(xiàn)、其中接收者是實(shí)例字段并且變量是整型的易失性設(shè)置的實(shí)現(xiàn),以及各種組合等。
在一些實(shí)施例中,使用用于每個(gè)接口形狀的分開的抽象類來實(shí)現(xiàn)VarHandle,從而造成九個(gè)不同抽象類的集合。然后這些抽象類可以由實(shí)現(xiàn)用于給定訪問類型的受約束操作方法(包括用于其的代碼)的類來進(jìn)行子類化(sub-class)。例如,一個(gè)子類可以實(shí)現(xiàn)接收者是數(shù)組并且變量是泛型Object的情況,而另一個(gè)子類可以實(shí)現(xiàn)接收者是數(shù)組并且變量是靜態(tài)類型對象的情況。二者共享相同的接口形狀并且因此可以派生自同一抽象類,但是提供不同的實(shí)現(xiàn)。然而,為了簡化對VarHandle的訪問,一些實(shí)施例為VarHandle定義包括用于每個(gè)受約束操作的多態(tài)方法簽名的單個(gè)抽象類。例如,在Java代碼中,抽象類可以部分地看起來是:
public abstract class VarHandle{
...
//放寬的訪問器
public final native
@MethodHandle.PolymorphicSignature
Object get(Object...args);
public final native
@MethodHandle.PolymorphicSignature
Object set(Object...args);
//易失性訪問器
public final native
@MethodHandle.PolymorphicSignature
Object getVolatile(Object...args);
public final native
@MethodHandle.PolymorphicSignature
Object setVolatile(Object...args);
//懶惰的訪問器
public final native
@MethodHandle.PolymorphicSignature
Object getAcquire(Object...args);
public final native
@MethodHandle.PolymorphicSignature
Object setRelease(Object...args);
//比較和設(shè)置訪問器
public final native
@MethodHandle.PolymorphicSignature
Object compareAndSet(Object...args);
public final native
@MethodHandle.PolymorphicSignature
Object getAndSet(Object...args);
...}
以下示例引用VarHandle以便于說明清楚的示例,但是本文所描述的技術(shù)不限于如在VarHandle示例中呈現(xiàn)的類的確切格式。
在實(shí)施例中,多態(tài)簽名是指示方法可以接受任何任意數(shù)量的變元和變元類型以及可以具有任何返回類型的特殊類型的簽名。因此,實(shí)現(xiàn)對應(yīng)于多態(tài)簽名的方法的VarHandle的子類可以潛在地使用任何接口形狀并且仍然通過驗(yàn)證。因此,一個(gè)抽象類足以覆蓋不同的訪問類型,并且開發(fā)者可以以相對統(tǒng)一的方式利用VarHandle的任何子類的受約束的操作方法。
在實(shí)施例中,VarHandle子類保持與受約束操作的四個(gè)可能方法描述符對應(yīng)的四個(gè)方法類型描述符以及保持“成員名(membername)”的“varform”。例如,用于“-get(獲得)”操作的描述符可以是相同的,類似地用于“-set(設(shè)置)”操作的描述符也可以是相同的,因?yàn)檫@些方法的變元和返回類型匹配。由VarHandle子類保持的方法類型描述符決定所實(shí)現(xiàn)的受約束操作方法的方法簽名。因此,為了正確地使用受約束操作方法,調(diào)用點(diǎn)使用由對應(yīng)描述符指定的變量和返回類型來啟用該方法。“varform”表示不同受約束操作的行為。因此,在示例實(shí)施例中,varform保持八個(gè)“成員名”,每個(gè)成員名表征不同的受約束操作方法的行為。例如,對于每個(gè)受約束操作,對應(yīng)的“成員名”可以表示用于執(zhí)行用于所實(shí)現(xiàn)的訪問類型的受約束操作的代碼或指令。
3.3VarHandle過程流程字段例
圖5示出了根據(jù)實(shí)施例的以框圖形式的用于使用VarHandle執(zhí)行受約束操作的示例過程。為了本節(jié)的目的,利用VarHandle的類將被稱為“作用類(acting class)”,并且將假設(shè)使用示例VarHandle抽象類來定義VarHandle。
在下面的示例中,將參考訪問類的實(shí)例字段的VarHandle來描述圖5。針對數(shù)組訪問使用圖5的過程的附加示例將稍后在節(jié)3.4中描述。
在方框500處,虛擬機(jī)104接收VarHandle實(shí)例的聲明。在一些實(shí)施例中,虛擬機(jī)104經(jīng)由已從用于作用類的源代碼文件101編譯而來的類文件103接收VarHandle實(shí)例的聲明。在實(shí)施例中,可以使用以下示例源代碼來聲明用于由接收者“Receiver”保持的類型“Value”的字段“val”的VarHandle實(shí)例:
VarHandle varHandleOfValueOnReceiver=VarHandles.lookup()
.findFieldHandle(Receiver.class,"val",Value.class);
在實(shí)施例中,VarHandles是如下生成器類:其包括用于返回實(shí)現(xiàn)用于特定訪問類型的受約束操作的各種VarHandle子類的實(shí)例的方法。lookup()是類VarHandle的靜態(tài)方法,它返回被用于返回(經(jīng)由方法findFieldHandle)連結(jié)到(tiedto)指定字段的VarHandle子類實(shí)例的查找類。例如,由查找類生成的VarHandle子類實(shí)例可以存儲與VarHandle子類實(shí)例所綁定到的接收者和變量類對應(yīng)的用于每個(gè)方法的描述符。在上面的示例聲明中,findFieldHandle創(chuàng)建訪問具有Receiver類型的靜態(tài)類型對象的名為“val”的字段的VarHandle實(shí)例,其中該字段保持具有Value類型的靜態(tài)類型對象。因此,例如,對于指派給varhandleOfValueOnReceiver的VarHandle來說,“-set”操作可以與描述符“(L Receiver L Value)V”相關(guān)聯(lián),而“-get”操作可以與描述符“(L Receiver)L Value”相關(guān)聯(lián)。
作為另一個(gè)示例,在用于類的靜態(tài)字段的VarHandle的情況下,查找類方法可以采用類的名稱(或者表示該類的Class對象的名稱)、在該類內(nèi)的靜態(tài)字段的名稱以及靜態(tài)字段的類。在一些實(shí)施例中,查找類執(zhí)行訪問控制檢查,以查明作用類是否具有訪問指定的接收者和變量的適當(dāng)特權(quán)。例如,如果變量被聲明為私有的,則在定義該變量的包外部的類可能沒有足夠的特權(quán)來訪問該變量,這將導(dǎo)致查找類生成錯(cuò)誤。在一些實(shí)施例中,在創(chuàng)建VarHandle時(shí)而不是在啟用VarHandle的方法時(shí)執(zhí)行訪問檢查。在一些情況下,在查找時(shí)執(zhí)行訪問控制檢查通過允許VarHandle在啟用期間省略這些訪問檢查來減少開銷。因此,在這樣的實(shí)施例中,有可能將VarHandle傳遞到否則將不具有對所引用的接收者/變量的訪問許可的類。然而,在一些實(shí)施例中,在啟用期間可以再次執(zhí)行訪問檢查,以防止具有不足的許可的類啟用VarHandle,其代價(jià)是低效的運(yùn)行時(shí)性能。
在方框501處,虛擬機(jī)104接收使用VarHandle執(zhí)行受約束操作的指令。例如,對varHandleOfValueOnReceiver的易失性設(shè)置操作可以看起來如下:
Receiver r=...
Value v=...
varHandleOfValueOnReceiver.setVolatile(r,v)
其中r是類Receiver的實(shí)例,v是類Value的實(shí)例,并且使用r和v為參數(shù)對varHandleOfValueOnReceiver啟用方法setVolatile。在實(shí)施例中,當(dāng)編譯器102構(gòu)建用于作用類的類文件200時(shí),常量表201的方法引用結(jié)構(gòu)207包括對類VarHandle、方法的名稱setVolatile以及表示采用類型為Receiver的對象和類型為Value的對象作為參數(shù)并且具有返回類型void的方法的符號方法描述符“(L Receiver L Value)V”的引用。此外,在虛擬機(jī)104代碼中,調(diào)用被轉(zhuǎn)換為指定對于上面提到的對setVolatile的方法引用的到常量表201的索引的啟用指令。
作為另一個(gè)示例,在用于類的靜態(tài)字段的VarHandle的情況下,setVolatile可以被隱式地綁定到類,從而消除了顯式指定接收者的需要。例如,調(diào)用可以看起來是“varHandleOfValueOnReceiver.setVolatile(v)”。在聲明期間,VarHandle被綁定到接收者的類/類型,并且因?yàn)檫@是靜態(tài)訪問,所以僅存在一個(gè)可能的接收者(該類自身)。因此,與實(shí)例字段情況不同,VarHandle已經(jīng)知道要對其作用的確切接收者。然而,在實(shí)例字段情況下,VarHandle被綁定到接收者的類型/類,但是不綁定到任何特定的接收者實(shí)例。因此,調(diào)用特定受約束函數(shù)/方法的指令通過隱式綁定到接收者(諸如在靜態(tài)情況下)來指示接收者或者指定接收者(諸如在實(shí)例字段的情況下)。因此,執(zhí)行實(shí)例訪問的VarHandle能夠?qū)Ρ粋鬟f到調(diào)用中的任何接收者實(shí)例執(zhí)行操作,只要該接收者匹配在聲明期間VarHandle被綁定到的類型。
作為另一個(gè)示例,對varHandleOfValueOnReceiver的易失性獲得操作可以看起來如下:
Receiver r=...
Value v=(Value)varHandleOfValueOnReceiver.getVolatile(r);其中r是Receiver的實(shí)例,v是值的實(shí)例,(Value)是對返回的類型轉(zhuǎn)換,并且使用r作為參數(shù)對varHandleOfValueOnReceiver啟用getVolatile。在實(shí)施例中,當(dāng)編譯器102構(gòu)造用于作用類的類文件200時(shí),常量表201的方法引用結(jié)構(gòu)207包括對類VarHandle、方法的名稱getVolatile以及表示采用類型為Receiver的對象作為參數(shù)并且返回類型為Value的對象的方法的符號方法描述符“(L Receiver)L Value”的引用。此外,在虛擬機(jī)104代碼中,調(diào)用被轉(zhuǎn)換為指定對于上面提到的對getVolatile的方法引用的到常量表201的索引的啟用指令。在一些實(shí)施例中,getVolatile將靜態(tài)類型的對象向下擦除(erase down)為最一般的形式(類型Object),因此返回時(shí)的類型轉(zhuǎn)換確保編譯器102可以確定用于符號方法描述符“(L Receiver)L Value”的返回類型。然而,在其它實(shí)施例中,如果編譯器102可以從表達(dá)式左側(cè)的局部變量類型推斷出返回類型(例如,編譯器實(shí)現(xiàn)“目標(biāo)類型化”),則可以避免顯式的類型轉(zhuǎn)換。
在方框502處,虛擬機(jī)104對受約束操作執(zhí)行類型安全檢查。在實(shí)施例中,當(dāng)引用多態(tài)簽名方法的啟用指令由虛擬機(jī)104執(zhí)行時(shí),用被稱為“內(nèi)部鏈接”的過程來替換上文在節(jié)2.3中描述的鏈接步驟。在正常情況下,使用易失性設(shè)置操作作為示例,當(dāng)虛擬機(jī)104試圖解析符號引用卻發(fā)現(xiàn)沒有與名稱setVolatile和描述符“(L Receiver L Value)V”相匹配的方法在類VarHandle中被定義時(shí),將發(fā)生鏈接錯(cuò)誤。然而,響應(yīng)于確定被啟用的方法是簽名多態(tài)的,虛擬機(jī)104在執(zhí)行各種類型安全檢查的調(diào)用點(diǎn)處執(zhí)行與啟用的訪問類型相關(guān)聯(lián)的預(yù)定義方法,并且執(zhí)行與被啟用方法對應(yīng)的成員名。
例如,多態(tài)簽名方法可以與標(biāo)志或者屬性的特定組合相關(guān)聯(lián),諸如在VarHandle類的類文件200中將方法設(shè)置為最終的(final)和本機(jī)的(native)二者。當(dāng)啟用方法引用時(shí),虛擬機(jī)104檢查標(biāo)志,以確定該方法是否是簽名多態(tài)的。如果是,則虛擬機(jī)104調(diào)用被設(shè)計(jì)為處理由方法引用的描述符指定的特定接口形狀的預(yù)定義方法。例如,預(yù)定義方法可以將類向下擦除為泛型Object,以便通過允許向下擦除為同一接口形狀的不同啟用使用同一預(yù)定義方法來減少預(yù)定義方法的數(shù)量。否則,方法不是簽名多態(tài)的,并且虛擬機(jī)104繼續(xù)進(jìn)行如上文在節(jié)2.3中所描述的鏈接。
在一些實(shí)施例中,虛擬機(jī)104操縱操作數(shù)棧402,以向預(yù)定義方法提供附加信息。例如,由于在內(nèi)部鏈接期間繞過了平常的鏈接程序,因此不能保證提供給被啟用的多態(tài)簽名方法的變元匹配在VarHandle聲明期間指定的類型。這可能導(dǎo)致非預(yù)期的行為,因?yàn)楸粏⒂靡詧?zhí)行受約束操作的VarHandle實(shí)例的成員名可能依賴于在VarHandle聲明期間指定的接收者和變量類的大小和/或訪問類型。在啟用setVolatile方法時(shí),操作數(shù)棧402將具有被推送到其上的r、v和varHandleofValueonReceiver。然而,虛擬機(jī)104可以將諸如對由啟用指令索引的方法引用結(jié)構(gòu)的描述符的引用之類的附加數(shù)據(jù)推送到棧上。因此,在預(yù)定義的方法中,虛擬機(jī)104可以將由VarHandle實(shí)例存儲的描述符與調(diào)用點(diǎn)處的方法啟用的描述符進(jìn)行比較,以確保變元和返回類型與VarHandle實(shí)例的成員名所期望的變元和返回類型匹配。如果匹配,則預(yù)定義方法啟用成員名以執(zhí)行受約束的操作。否則,生成錯(cuò)誤。
因此,在一些實(shí)施例中,內(nèi)部鏈接分兩個(gè)部分執(zhí)行:(1)虛擬機(jī)104基于方法引用的被擦除的描述符來自動(dòng)啟用預(yù)定義方法,以執(zhí)行類型安全檢查,以及(2)虛擬機(jī)104啟用VarHandle實(shí)例的成員名,以執(zhí)行受約束的操作。在一些實(shí)施例中,在步驟(2)期間,執(zhí)行附加的安全檢查,諸如確定接收者是否為null或者(在數(shù)組訪問的情況下)確定指定的索引是否在界限內(nèi)。
當(dāng)句柄被常量折疊(constant folded)時(shí),易失性設(shè)置操作的示例內(nèi)聯(lián)蹤跡(inlining trace)如下:
另外,用于處理“(L Object,L Object)V”的被擦除的簽名的用于setVolatile的示例預(yù)定義方法可以看起來如下:
在示例方法setVolatile_LL_V中,第一參數(shù)是作為類VarHandle的實(shí)例的handle,第二參數(shù)是接收者,第三參數(shù)是值,而最后一個(gè)參數(shù)是符號方法描述符。示例方法setVolatile_LL_V首先執(zhí)行方法類型描述符檢查,以確定在調(diào)用點(diǎn)處的符號描述符(在作用類處的方法調(diào)用)是否與用于由handle存儲的用于“-set(設(shè)置)”操作的調(diào)用描述符匹配。如果描述符不匹配,則生成錯(cuò)誤,諸如拋出將由異常處理程序捕獲的異常。否則,將利用除了在內(nèi)部鏈接期間被添加的symbolicMethodType之外的所有參數(shù)來啟用MethodHandle.linkToStatic方法以輔助預(yù)定義方法。然而,附加參數(shù)handle.vform.setVolatile被添加到變元,它表示實(shí)現(xiàn)受約束的操作的成員名。try/catch塊存在,以捕獲可能由MethodHandle.linkToStatic啟用拋出的異常。然而,在其它實(shí)施例中,可以通過利用避免這樣的聲明的內(nèi)部鏈接方法來避免try/catch塊。在實(shí)施例中,對內(nèi)部鏈接方法使用@ForceInline,它通知解釋器108的JIT編譯器不管最大內(nèi)聯(lián)限制和方法大小如何都內(nèi)聯(lián)該方法。
在方框503處,虛擬機(jī)104鏈接到實(shí)現(xiàn)受約束操作的方法代碼。在實(shí)施例中,方法MethodHandle.linkToStatic鏈接由與易失性設(shè)置相關(guān)聯(lián)的“成員名”表征的方法(在上面的示例中為handle.vform.setVolatile)。因此,MethodHandle.linkToStatic使得虛擬機(jī)104執(zhí)行解析,從而將作用類中對setVolatile的方法引用解析為處理所聲明的varHandle的訪問類型的子類的setVolatile方法代碼。在這種情況下,示例聲明使用指派給VarHandle子類型FieldInstanceRefHandle的lookup()方法并且因此linkToStatic將引用鏈接到實(shí)現(xiàn)受約束操作setVolatile的FieldInstanceRefHandle的方法代碼。對于字段實(shí)例訪問實(shí)現(xiàn)setVolatile的鏈接代碼的示例如下:
第一個(gè)參數(shù)是“FieldInstanceRefHandle”實(shí)例而后續(xù)參數(shù)是傳遞給“VarHandle.setVolatile”方法的參數(shù)?!癋ieldInstanceRefHandle”實(shí)例保持要與“Unsafe.putObjectVolatile”的啟用一起使用的字段偏移。在該啟用之前:執(zhí)行安全檢查,以確保接收者實(shí)例不是“null”(以避免潛在的分段錯(cuò)誤);并且作為附加的安全檢查來執(zhí)行值的轉(zhuǎn)換檢查。
在方框504處,虛擬機(jī)104執(zhí)行所鏈接的方法代碼,以執(zhí)行受約束操作。在上面的示例中,所鏈接的方法代碼經(jīng)由對Unsafe.putObjectVolatile的調(diào)用來執(zhí)行易失性設(shè)置操作,其中Unsafe.putObjectVolatile以指定的受約束方式將value存儲在對應(yīng)的存儲器位置處。例如,虛擬機(jī)104可以經(jīng)由對操作系統(tǒng)110的一個(gè)或多個(gè)系統(tǒng)調(diào)用或呈現(xiàn)給底層計(jì)算機(jī)系統(tǒng)的處理器的機(jī)器指令來執(zhí)行受約束操作。例如,為了實(shí)現(xiàn)存儲器圍欄操作,除了對變量的存儲器位置的加載和/或存儲之外,虛擬機(jī)104還可以發(fā)出由底層硬件本機(jī)支持的一條或多條存儲器屏障指令。在一些實(shí)施例中,底層計(jì)算機(jī)硬件的處理器可以不支持執(zhí)行受約束操作的本機(jī)操作。然而,在這樣的實(shí)施例中,虛擬機(jī)104可以將綜合了受約束操作將產(chǎn)生的相同效果的多個(gè)系統(tǒng)調(diào)用或機(jī)器指令拼湊(cobble)在一起。
3.4VARHANDLE過程流數(shù)組例
以下是被應(yīng)用于接收者是數(shù)組并且變量是靜態(tài)類型對象的情況的圖5的過程流的示例。為了本節(jié)的目的,利用VarHandle的類將被稱為“作用類”,并且將假設(shè)使用示例VarHandle抽象類來定義VarHandle。
在方框500處,虛擬機(jī)104接收VarHandle的聲明。在實(shí)施例中,使用以下示例源代碼來聲明由數(shù)組類型“Value[]”的接收者保持的組件類型“Value”的數(shù)組元素的VarHandle實(shí)例:
VarHandle varHandleOfValueArray=VarHandles.
arrayHandle(Value[].class);
其中VarHandles.arrayHandle返回被實(shí)現(xiàn)以處理數(shù)組訪問的VarHandle子類的實(shí)例。
在方框501處,虛擬機(jī)104接收經(jīng)由VarHandle執(zhí)行受約束操作的指令。在實(shí)施例中,經(jīng)由以下示例源代碼來啟用對數(shù)組類型Value[]的實(shí)例r的索引i處的數(shù)組元素Valuev的易失性設(shè)置操作:
Value[]r=...
int i=...
Value v=...
varHandleOfValueArray.setVolatile(r,i,v)
在實(shí)施例中,對setVolatile(r,i,v)的方法引用包括方法描述符“(LReceiverILValue)V”,其表示該方法采用類型為Receiver的對象、整數(shù)和類型為Value的對象,并且返回類型為void。
在實(shí)施例中,通過以下示例源代碼來啟用對來自數(shù)組類型Value[]的實(shí)例r的索引i處的數(shù)組元素的Valuev的易失性獲得操作:
Value[]r=...
int i=...
Value v=(Value)varHandleOfValueArray.getVolatile(r,i);
對應(yīng)的符號方法描述符是“(LReceiverI)LValue”,其匹配易失性獲得操作的方法類型描述符。
在方框502處,虛擬機(jī)104對該操作執(zhí)行類型安全檢查。在實(shí)施例中,對“VarHandle.setVolatile”的啟用內(nèi)部鏈接到“VarHandle.setVolatile_LIL_V”:
在VarHandle.setVolatile_LIL_V中,類型安全檢查由checkExactType執(zhí)行,其確保handle的方法描述符匹配在調(diào)用點(diǎn)處使用的參數(shù)/返回值的symbolicMethodType。
在實(shí)施例中,“MethodHandle.linkToStatic”的啟用鏈接到由與易失性設(shè)置相關(guān)聯(lián)的“成員名”表征的方法。在這種情況下,handle經(jīng)由Varhandles.arrayHandle生成,Varhandles.arrayHandle返回VarHandle的ArrayRefHandle子類型并且因此成員名鏈接到setVolatile方法的數(shù)組實(shí)現(xiàn)。在實(shí)施例中,所鏈接的方法的示例實(shí)現(xiàn)如下:
“ArrayRefHandle”實(shí)例保持?jǐn)?shù)組基地址偏移和數(shù)組移位,以便從索引計(jì)算要與“Unsafe.putObjectVolatile”的啟用一起使用的偏移。在該啟用之前:執(zhí)行安全檢查,以確保數(shù)組不為“null”并且索引在數(shù)組界限內(nèi)。
3.5優(yōu)化
在實(shí)施例中,虛擬機(jī)104執(zhí)行用于VarHandle的優(yōu)化,以減輕由于內(nèi)部鏈接、類型檢查和安全檢查而施加的負(fù)擔(dān)。
作為一個(gè)示例,可以由虛擬機(jī)104執(zhí)行的優(yōu)化是“常量折疊(constant folding)”。常量折疊是在編譯時(shí)而不是在執(zhí)行期間評估涉及常量的表達(dá)式的優(yōu)化技術(shù)。因此,例如,解釋器108/JIT編譯器可以用存儲結(jié)果的指令替換評估常量的指令。例如,“i=100+105+109”一般會把值推送到棧上并且執(zhí)行相加指令的啟用。然而,虛擬機(jī)104可以替代地提前評估表達(dá)式并且用將“314”加載到局部變量i中的單個(gè)指令來替換這些指令。因此,當(dāng)虛擬機(jī)104指令被轉(zhuǎn)換為機(jī)器代碼以供執(zhí)行時(shí),由于執(zhí)行更少和更簡單的操作,所以運(yùn)行時(shí)速度增加。
作為另一個(gè)示例,可以由虛擬機(jī)104執(zhí)行的優(yōu)化是“內(nèi)聯(lián)”或“內(nèi)聯(lián)展開”。在對虛擬機(jī)104或其組件(諸如解釋器108/JIT編譯器)進(jìn)行內(nèi)聯(lián)期間,用被調(diào)用方法的主體來替換在調(diào)用點(diǎn)處的方法啟用。因此,虛擬機(jī)104繞過了處理該啟用以及使執(zhí)行從一個(gè)方法的主體的跳轉(zhuǎn)到另一個(gè)方法的主體的開銷。在實(shí)施例中,當(dāng)虛擬機(jī)104將調(diào)用內(nèi)部鏈接到對VarHandle的受約束操作時(shí),虛擬機(jī)104可以通過內(nèi)聯(lián)在內(nèi)部鏈接期間執(zhí)行的代碼中的一些或全部來優(yōu)化。
作為另一個(gè)示例,可以由虛擬機(jī)104執(zhí)行的優(yōu)化是高速緩存所解析的VarHandle啟用并且對于將來的啟用重用該存儲器位置。假設(shè)識別綁定的接收者和變量類型VarHandle的字段是靜態(tài)的,則不需要再次執(zhí)行在查找期間的訪問許可檢查以及由預(yù)定義方法執(zhí)行的類型安全檢查。因此,當(dāng)下次啟用同一VarHandle方法時(shí),可以跳過這些檢查,從而得到幾乎與直接暴露“不安全”方法持平的運(yùn)行時(shí)性能。
作為另一個(gè)示例,實(shí)施例可以將對VarHandle的引用聲明為一旦設(shè)置就不能改變的常量值。例如,在Java中,一些實(shí)現(xiàn)使用“static final(靜態(tài)最終)”屬性來定義這樣的字段。因此,使用Java示例,對VarHandle實(shí)例“V”的static final引用被認(rèn)為是常量,因此關(guān)于可以經(jīng)由引用依賴關(guān)系鏈被追溯到V的類的任何final實(shí)例字段也被認(rèn)為是常量。因此,當(dāng)虛擬機(jī)104內(nèi)聯(lián)對V的方法調(diào)用時(shí),虛擬機(jī)104可以將與類型檢查相關(guān)聯(lián)的指令常量折疊起來(fold away),link-to-static(鏈接到靜態(tài))調(diào)用可以被內(nèi)聯(lián)(因?yàn)橐阎詈笠粋€(gè)變元是常量),并且顯式的轉(zhuǎn)換檢查也可以折疊起來。
3.6放寬關(guān)于多態(tài)簽名方法的返回類型
在一些實(shí)施例中,可以放寬多態(tài)簽名方法(諸如在先前的VarHandle示例中所使用的多態(tài)簽名方法)的返回類型,以使得如果類型不是泛型Object,則該類型是被編碼成用于該方法的符號類型描述符的返回類型。例如,在說明性實(shí)施例中,VarHandle類可以被修改以聲明放寬的多態(tài)簽名方法,以使得所有基于設(shè)置的方法都返回“void”,而比較和設(shè)置方法返回“布爾型”。因此,在一些情況下,因?yàn)榉祷仡愋涂梢詮姆椒ê灻茢喑?,所以可以避免上面的示例中的類型轉(zhuǎn)換。表示用于VarHandle的放寬的多態(tài)方法簽名的示例源代碼部分如下:
abstract class VarHandle{
...
public final native
@MethodHandle.PolymorphicSignature
voidset(Object...args);
...
public final native
@MethodHandle.PolymorphicSignature
boolean compareAndSet(Object...args);
...
}
3.7通用多態(tài)簽名方法
在實(shí)施例中,多態(tài)簽名方法的類型的放寬被擴(kuò)展,以換取每個(gè)接收者類型聲明一個(gè)抽象類。因此,這樣的抽象類將能夠支持原語類型和引用值類型二者。
在實(shí)施例中,作為一個(gè)VarHandle抽象類的代替,定義對應(yīng)于每種類型的接收者的三個(gè)類:
-用于靜態(tài)字段訪問的“StaticFieldHandle<R,V>”
-用于實(shí)例字段訪問的“FieldHandle<R,V>”;和
-用于數(shù)組元素訪問的“ArrayHandle<R,I,V>”。
為接收者R和值V聲明類型變量。除了數(shù)組訪問之外,還存在用于到數(shù)組的索引I的類型變量。在一些實(shí)施例中,類型變量I確保針對大于由虛擬機(jī)104定義的最大整數(shù)值的索引的對大數(shù)組中的元素的訪問可以被支持。此外,與VarHandle一樣,用于數(shù)組的接收者可以是數(shù)組類,或者可以是用于類似數(shù)組的結(jié)構(gòu)的某種其它類型,諸如對堆外存儲器的訪問。
在靜態(tài)字段訪問可能很少的一些實(shí)施例中,這樣的訪問可以被折疊成“FieldHandle<R,V>”,其中受約束的訪問方法接受空值或忽略接收者的值。因此,用于接收者的空值可以充當(dāng)指示是否對所聲明的VarHandle使用靜態(tài)字段訪問或?qū)嵗侄卧L問的標(biāo)志。因此,可以以用于確定是否正在執(zhí)行靜態(tài)訪問或字段訪問的附加處理為代價(jià)來減少類的數(shù)量。
在一些實(shí)施例中,上文描述的類可以從VarHandle擴(kuò)展,以保留較低的級別但是更通用的機(jī)制,因?yàn)殛P(guān)于子類的方法將實(shí)質(zhì)上重寫超類的那些方法。
在實(shí)施例中,方法對于受約束操作通過其被內(nèi)部鏈接的機(jī)制保持與先前在節(jié)3.3中描述的相同,并且因此性能將保持與VarHandle示例相對持平。然而,在一些情況下,可用性可以被增強(qiáng),因?yàn)樵诰幾g時(shí)而不是運(yùn)行時(shí)可能捕獲更多的錯(cuò)誤,這是由于在一些實(shí)現(xiàn)中,簽名多態(tài)方法的啟用可以由編譯器102編譯,而不管用于該啟用的描述符是否產(chǎn)生有效的實(shí)現(xiàn)方法。
考慮用于對訪問類型為Receiver的實(shí)例上的int字段的FieldHandle的啟用的以下示例代碼:
FieldHandle<Receiver,Integer>fh=...
int a=1;
int b=2;
fh.compareAndSet(this,1,2);
Integer ba=a;
Integer bb=b;
fh.compareAndSet(this,ba,bb);
在該示例中,原語類型被表示為裝箱的(boxed)類型。在表示原始值的對象中封裝(package)原始值被稱為“裝箱”。同樣,將裝箱的值解包回原始形式被稱為“拆箱”。在一些實(shí)施例中,諸如整型、字節(jié)型、字符型、長整型等之類的原語類型不能被擦除為較通用的形式。然而,裝箱的形式可以被擦除為泛型Object。因此,通過將原始值裝箱,原始值可以被一般化。
在一些實(shí)施例中,第一個(gè)“compareAndSet”操作編譯為以下示例虛擬機(jī)104代碼:
10:invokevirtual#2//Method java/lang/invoke/FieldHandle.compareAndSet:(LVarHandleT est;II)Z
并且第二個(gè)編譯為:
32:invokevirtual#4//Method java/lang/invoke/FieldHandle.compareAndSet:(LVarHandleT est;Ljava/lang/Integer;Ljava/lang
其中第一個(gè)數(shù)字(“10”/“32”)表示虛擬機(jī)104代碼的索引,invoke virtual是啟用采用指示常量表201中的方法引用的索引的變元的實(shí)例方法的命令,并且注釋包括包含該方法的類的完全限定的路徑名稱和正在被啟用的方法的描述符。
由于原語類型“int”與它的裝箱的類型“Integer”兼容,因此編譯器102將成功地編譯第一啟用,并且因此符號方法描述符將等于比較和設(shè)置操作的方法描述符(例如,在方法是簽名多態(tài)的情況下,編譯器103將驗(yàn)證“int”可轉(zhuǎn)換為“Integer”,但是將不生成裝箱指令)。對于第二次啟用,符號方法描述符將不同,并且因此可能導(dǎo)致運(yùn)行時(shí)異常。然而,invoke-exact語義可以被放寬,以替代地對變元執(zhí)行變換,以便使得在調(diào)用點(diǎn)處的描述符與用于比較和設(shè)置操作的描述符相匹配。例如,值可以在執(zhí)行基本啟用之前和/或之后被自動(dòng)地裝箱或拆箱。
在一些實(shí)施例中,變換被擴(kuò)展到接收者類型和/或非原始值類型,諸如從字符數(shù)組轉(zhuǎn)換而來的字符串。另外,在一些實(shí)施例中,編譯器102將類型參數(shù)的類(如果已知的話)編碼為符號方法描述符,而不是可能的子類型。
在實(shí)施例中,VarHandle實(shí)例的方法中的一個(gè)或多個(gè)方法被定義為具有通用簽名,并且虛擬機(jī)104為例如編譯后的Java字節(jié)碼中的VarHandle實(shí)例的方法中的一些或所有方法在調(diào)用點(diǎn)處具體化(reify)通用簽名。
例如,考慮以下Java代碼:
上面的類中的fh.compareAndSet的啟用可以被編譯為以下指令:
32:invokevirtual#6//方法
Test$FieldHandle.compareAndSet:(Ljava/lang/Object;
Ljava/lang/Object;Ljava/lang/Object;)Z
在上面的示例中,與類型變量“R”和“V”相關(guān)聯(lián)的參數(shù)類型(在這種情況下是用于啟用的Receiver和Value的實(shí)例)被擦除為泛型Object。在具體化通用簽名被實(shí)現(xiàn)的實(shí)施例中,Receiver和Value可以在調(diào)用點(diǎn)處被捕獲,而不是擦除它們,并且如果知道被傳遞給該方法的參數(shù)的類型是正確的,則在進(jìn)一步繼續(xù)之前,可以執(zhí)行調(diào)用點(diǎn)處的方法簽名(符號類型描述符)與期望的方法類型描述符的高效的指針相等性檢查(就像在多態(tài)簽名的情況下那樣)。
3.8代碼VARHANDLE和方法句柄的合并
在一些實(shí)施例中,除了VarHandle之外,虛擬機(jī)還實(shí)現(xiàn)充當(dāng)對特定方法的句柄的、被稱為MethodHandle的另一訪問結(jié)構(gòu)。MethodHandle實(shí)現(xiàn)的示例可以在公開的Java文檔中找到。(http://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodH andle.html)
與VarHandle一樣,MethodHandle的一些實(shí)施例利用多態(tài)簽名和內(nèi)部鏈接。例如,啟用MethodHandle被綁定到的方法可以通過對被聲明為使用lookup類的特定方法的MethodHandle實(shí)例使用.invoke([Parameters])來執(zhí)行。啟用內(nèi)部鏈接到MethodHandle的LamdaForm,該MethodHandle的LamdaForm本質(zhì)上充當(dāng)實(shí)現(xiàn)方法的包裝器,并且在啟用實(shí)現(xiàn)方法之前執(zhí)行各種動(dòng)作和/或檢查。
在一些實(shí)施例中,作為VarHandle保持Varform的代替,VarHandle保持對應(yīng)于不同的受約束操作方法的LambdaForms的集合。因此,“VarForm”實(shí)例可以被高速緩存并且在VarHandle和MethodHandle實(shí)例之間共享。此外,常見的“VarForm”實(shí)例可以靜態(tài)定義并且因此降低啟動(dòng)成本。LambdaForm本質(zhì)上充當(dāng)其引用可以被更新的成員名的箱子。當(dāng)LamdaForm從被解釋切換到(經(jīng)由編譯器102)被編譯時(shí),可能發(fā)生更新。具體而言對于靜態(tài)字段訪問,成員名指向如下實(shí)現(xiàn):其檢查類是否需要被初始化(并且如果需要的話就將類初始化)、訪問靜態(tài)字段,然后將成員名更新為不執(zhí)行靜態(tài)訪問并且僅訪問該字段的實(shí)現(xiàn)。此外,一些實(shí)施例可以實(shí)現(xiàn)用于返回對于特定受約束操作的MethodHandle的VarHandle的方法。
在一些實(shí)施例中,作為可替代物,可以生成參考VarHandle實(shí)例上的方法的基于字段的直接方法句柄,以便以新形式的類自旋為代價(jià)減少自旋(spinning)類的數(shù)量。
3.9存儲器圍欄
存儲器圍欄是使得諸如CPU之類的處理器或編譯器對在屏障指令之前和之后發(fā)出的存儲器操作實(shí)施排序約束的一種類型的屏障指令。因此,在該屏障之前發(fā)出的操作被保證在該指令之后發(fā)出的操作之前執(zhí)行。采用存儲器圍欄的一個(gè)原因是由于CPU和編譯器常常采用可能導(dǎo)致無序執(zhí)行的性能優(yōu)化的事實(shí)。對于單個(gè)執(zhí)行線程來說,優(yōu)化通常被設(shè)計(jì)為確保程序的語義不受干擾,諸如省略對于從未由程序?qū)嶋H使用的數(shù)據(jù)的加載指令或者從緩沖區(qū)或高速緩存而不是從主存儲器檢索值。然而,對于多線程程序來說,除非仔細(xì)控制,否則存儲器操作的重新排序可能導(dǎo)致不可預(yù)測的行為。存儲器圍欄不一定將數(shù)據(jù)存儲或加載到存儲器位置,而是控制何時(shí)可以從緩沖區(qū)/高速緩存讀取值以及何時(shí)緩沖區(qū)/高速緩存應(yīng)當(dāng)被沖刷到主存儲器。
幾乎所有處理器至少支持保證在圍欄之前發(fā)起的所有加載和存儲將被嚴(yán)格排序在圍欄之后發(fā)起的任何加載或存儲之前的通用屏障指令。然而,許多處理器還支持提供較少排序保證但是常常產(chǎn)生較少開銷的更細(xì)粒度的屏障。
在實(shí)施例中,虛擬機(jī)104支持可以訪問底層硬件架構(gòu)(或虛擬化的軟件構(gòu)造(construct),諸如虛擬機(jī)104)的各種防護(hù)操作的VarHandle。例如,VarHandle可以被擴(kuò)展為包括附加的方法,該方法作為調(diào)用執(zhí)行原子操作的“不安全”方法的代替內(nèi)部鏈接到發(fā)出屏障指令的成員名。由VarHandle支持的確切的屏障指令可以由硬件的底層架構(gòu)決定。例如,底層處理器或多處理器可以支持等同于所期望的存儲器圍欄的機(jī)器級指令。然而,在其它實(shí)施例中,底層硬件可能不支持所期望的存儲器圍欄。因此,在一些實(shí)施例中,虛擬機(jī)104通過在軟件中實(shí)現(xiàn)圍欄來合成存儲器圍欄操作,從而基本上使用其它機(jī)器級指令的組合來實(shí)現(xiàn)相同的效果。
以下是可以經(jīng)由VarHandle訪問的存儲器圍欄的幾個(gè)示例。
(1)LoadLoad(加載加載)屏障–序列(Load1;LoadLoad;Load2),確保Load1的數(shù)據(jù)在Load2訪問的數(shù)據(jù)和所有后續(xù)加載指令被加載之前被加載。在一些實(shí)施例中,LoadLoad屏障被用于執(zhí)行推測加載和/或其中等待加載指令可以繞過等待存儲的無序處理的處理器。
(2)StoreStore(存儲存儲)屏障–序列(Store1;StoreStore;Store2),確保Store1的數(shù)據(jù)在與Store2和所有后續(xù)存儲指令相關(guān)聯(lián)的數(shù)據(jù)之前對其它處理器可見。例如,常常對否則不保證從寫緩沖區(qū)和/或高速緩存到其它處理器或主存儲器的沖刷的嚴(yán)格排序的處理器使用StoreStore屏障。
(3)LoadStore(加載存儲)屏障–序列(Load1;LoadStore;Store2),確保Load1的數(shù)據(jù)在與Store2和所有后續(xù)存儲指令相關(guān)聯(lián)的所有數(shù)據(jù)之前被加載。例如,常常對否則不保證從寫緩沖區(qū)和/或高速緩存到其它處理器或主存儲器的沖刷的嚴(yán)格排序的處理器使用StoreStore屏障。
(4)StoreLoad(存儲加載)屏障–序列(Store1;StoreLoad;Load2),確保Store1的數(shù)據(jù)在由Load2和所有后續(xù)加載指令訪問的數(shù)據(jù)被加載之前對其它處理器可見。StoreLoad屏障防止后續(xù)的加載不正確地使用Store1的數(shù)據(jù)值而不是使用來自由不同處理器執(zhí)行的對同一位置的更近存儲的數(shù)據(jù)值。
在一些實(shí)施例中,虛擬機(jī)104在底層硬件本身不支持圍欄的情況下利用支持的屏障指令來合成圍欄。例如,假設(shè)底層硬件不提供對于釋放設(shè)置和獲取獲得懶惰操作的本機(jī)支持,而是支持上文列出的示例屏障指令??梢酝ㄟ^將加載-加載屏障指令與加載-存儲屏障指令組合來合成獲取圍欄,并且可以通過將存儲-加載屏障指令與存儲-存儲屏障指令組合來合成釋放圍欄。在一些實(shí)施例中,上面提到的獲取/釋放圍欄然后可以向硬件發(fā)出和/或被用來實(shí)現(xiàn)釋放設(shè)置/獲取獲得懶惰的受防護(hù)的操作。
3.10VARHANDLE功能的擴(kuò)展
雖然VarHandle已經(jīng)被描述為提供對原子操作和存儲器屏障指令的高效和安全訪問,但是VarHandle不限于這些功能。在其它實(shí)施例中,VarHandle可以被擴(kuò)展,以提供對幾乎任何類型的功能的訪問,諸如對沒有落入原子操作和存儲器屏障指令的類別內(nèi)的硬件特征的訪問。
4.0硬件概述
根據(jù)一個(gè)實(shí)施例,本文所描述的技術(shù)由一個(gè)或多個(gè)專用計(jì)算設(shè)備實(shí)現(xiàn)。專用計(jì)算設(shè)備可以被硬連線以執(zhí)行技術(shù),或者可以包括諸如被永久性地編程以執(zhí)行技術(shù)的一個(gè)或多個(gè)專用集成電路(ASIC)或現(xiàn)場可編程門陣列(FPGA)的數(shù)字電子設(shè)備,或者可以包括被編程為按照固件、存儲器、其它存儲裝置或者其組合中的程序指令來執(zhí)行技術(shù)的一個(gè)或多個(gè)通用硬件處理器。這樣的專用計(jì)算設(shè)備還可以將定制的硬連線邏輯、ASIC或FPGA與定制的編程組合來實(shí)現(xiàn)技術(shù)。專用計(jì)算設(shè)備可以是桌上型計(jì)算機(jī)系統(tǒng)、便攜式計(jì)算機(jī)系統(tǒng)、手持式設(shè)備、聯(lián)網(wǎng)設(shè)備或者結(jié)合硬連線和/或程序邏輯以實(shí)現(xiàn)技術(shù)的任何其它設(shè)備。
例如,圖6是示出可以在其上實(shí)現(xiàn)本發(fā)明的實(shí)施例的計(jì)算機(jī)系統(tǒng)600的框圖。計(jì)算機(jī)系統(tǒng)600包括總線602或者用于傳送信息的其它通信機(jī)制,以及與總線602耦接以用于處理信息的硬件處理器604。硬件處理器604可以是例如通用微處理器。
計(jì)算機(jī)系統(tǒng)600還包括被耦接到總線602以用于存儲信息和要由處理器604執(zhí)行的指令的主存儲器606,諸如隨機(jī)存取存儲器(RAM)或其它動(dòng)態(tài)存儲設(shè)備。主存儲器606還可以被用于在要由處理器604執(zhí)行的指令的執(zhí)行期間存儲臨時(shí)變量或其它中間信息。當(dāng)被存儲在處理器604可訪問的非暫態(tài)存儲介質(zhì)中時(shí),這樣的指令使計(jì)算機(jī)系統(tǒng)600成為被定制以執(zhí)行指令中指定的操作的專用機(jī)器。
計(jì)算機(jī)系統(tǒng)600還包括耦接到總線602以用于為處理器604存儲靜態(tài)信息和指令的只讀存儲器(ROM)608或者其它靜態(tài)存儲設(shè)備。諸如磁盤、光盤或固態(tài)驅(qū)動(dòng)器之類的存儲設(shè)備610被提供并且耦接到總線602,以用于存儲信息和指令。
計(jì)算機(jī)系統(tǒng)600可以經(jīng)由總線602耦接到諸如發(fā)光二極管(LED)顯示器之類的顯示器612,以用于向計(jì)算機(jī)用戶顯示信息。包括字母數(shù)字和其它鍵的輸入設(shè)備614被耦接到總線602,以用于向處理器604傳送信息和命令選擇。另一種類型的用戶輸入設(shè)備是諸如鼠標(biāo)、軌跡球或者光標(biāo)方向鍵之類的光標(biāo)控制器616,以用于向處理器604傳送方向信息和命令選擇以及用于控制顯示器612上的光標(biāo)移動(dòng)。這種輸入設(shè)備通常具有允許設(shè)備指定平面中的位置的在兩個(gè)軸(第一個(gè)軸(例如,x)和第二個(gè)軸(例如,y))上的兩個(gè)自由度。
計(jì)算機(jī)系統(tǒng)600可以使用與計(jì)算機(jī)系統(tǒng)結(jié)合使得計(jì)算機(jī)系統(tǒng)600成為專用機(jī)器或者把計(jì)算機(jī)系統(tǒng)600編程為專用機(jī)器的定制的硬連線邏輯、一個(gè)或多個(gè)ASIC或FPGA、固件和/或程序邏輯來實(shí)現(xiàn)本文所描述的技術(shù)。根據(jù)一個(gè)實(shí)施例,本文的技術(shù)由計(jì)算機(jī)系統(tǒng)600響應(yīng)于處理器604執(zhí)行包含在主存儲器606中的一條或多條指令的一個(gè)或多個(gè)序列而執(zhí)行。這樣的指令可以從諸如存儲設(shè)備610之類的另一存儲介質(zhì)被讀取到主存儲器606中。包含在主存儲器606中的指令序列的執(zhí)行使處理器604執(zhí)行本文所描述的過程步驟。在可替代的實(shí)施例中,硬連線的電路系統(tǒng)可以代替軟件指令或者與軟件指令結(jié)合使用。
如本文所使用的,術(shù)語“存儲介質(zhì)”指存儲使機(jī)器以特定方式操作的數(shù)據(jù)和/或指令的任何非暫態(tài)介質(zhì)。這樣的存儲介質(zhì)可以包括非易失性介質(zhì)和/或易失性介質(zhì)。非易失性介質(zhì)包括例如光盤、磁盤或固態(tài)驅(qū)動(dòng)器,諸如存儲設(shè)備610。易失性介質(zhì)包括動(dòng)態(tài)存儲器,諸如主存儲器606。存儲介質(zhì)的常見形式包括例如軟盤、柔性盤、硬盤、固態(tài)驅(qū)動(dòng)器、磁帶,或者任何其它磁性數(shù)據(jù)存儲介質(zhì),CD-ROM,任何其它光學(xué)數(shù)據(jù)存儲介質(zhì)、具有孔的圖案的任何物理介質(zhì)、RAM、PROM和EPROM、FLASH-EPROM、NVRAM、任何其它存儲器芯片或盒式磁帶。
存儲介質(zhì)與傳輸介質(zhì)不同但是可以與其結(jié)合使用。傳輸介質(zhì)參與在存儲介質(zhì)之間傳送信息。例如,傳輸介質(zhì)包括同軸線纜、銅線和光纖,這些同軸線纜、銅線和光纖包括包含總線602的配線。傳輸介質(zhì)還可以采取聲波或光波的形式,諸如在無線電波數(shù)據(jù)通信和紅外數(shù)據(jù)通信中產(chǎn)生的那些聲波或光波。
將一條或多條指令的一個(gè)或多個(gè)序列承載到處理器604以供執(zhí)行可以涉及各種形式的介質(zhì)。例如,指令最初可以被承載在遠(yuǎn)程計(jì)算機(jī)的磁盤或固態(tài)驅(qū)動(dòng)器上。遠(yuǎn)程計(jì)算機(jī)可以把指令加載到其動(dòng)態(tài)存儲器中并且使用調(diào)制解調(diào)器經(jīng)電話線發(fā)送指令。計(jì)算機(jī)系統(tǒng)600本地的調(diào)制解調(diào)器可以接收電話線上的數(shù)據(jù)并且使用紅外發(fā)射器把數(shù)據(jù)轉(zhuǎn)換為紅外信號。紅外檢測器可以接收紅外信號中承載的數(shù)據(jù),而適當(dāng)?shù)碾娐废到y(tǒng)可以把數(shù)據(jù)放在總線602上??偩€602把數(shù)據(jù)承載到主存儲器606,處理器604從主存儲器606檢索指令并且執(zhí)行指令。由主存儲器606接收的指令可以可選地在由處理器604執(zhí)行之前或之后被存儲在存儲設(shè)備610上。
計(jì)算機(jī)系統(tǒng)600還包括耦接到總線602的通信接口618。通信接口618提供耦接到網(wǎng)絡(luò)鏈路620的雙向數(shù)據(jù)通信,其中網(wǎng)絡(luò)鏈路620連接到局域網(wǎng)622。例如,通信接口618可以是提供到對應(yīng)類型電話線的數(shù)據(jù)通信連接的綜合業(yè)務(wù)數(shù)字網(wǎng)絡(luò)(ISDN)卡、線纜調(diào)制解調(diào)器、衛(wèi)星調(diào)制解調(diào)器或者調(diào)制解調(diào)器。作為另一個(gè)示例,通信接口618可以是提供到兼容的局域網(wǎng)(LAN)的數(shù)據(jù)通信連接的LAN卡。無線鏈路也可以被實(shí)現(xiàn)。在任何這樣的實(shí)現(xiàn)中,通信接口618發(fā)送和接收承載表示各種類型的信息的數(shù)字?jǐn)?shù)據(jù)流的電信號、電磁信號或光信號。
網(wǎng)絡(luò)鏈路620通常通過一個(gè)或多個(gè)網(wǎng)絡(luò)提供到其它數(shù)據(jù)設(shè)備的數(shù)據(jù)通信。例如,網(wǎng)絡(luò)鏈路620可以通過局域網(wǎng)622提供到主機(jī)計(jì)算機(jī)624或者到由互聯(lián)網(wǎng)服務(wù)提供商(ISP)626運(yùn)營的數(shù)據(jù)設(shè)備的連接。ISP 626又通過現(xiàn)在通常稱為“因特網(wǎng)”628的全球分組數(shù)據(jù)通信網(wǎng)絡(luò)提供數(shù)據(jù)通信服務(wù)。局域網(wǎng)622和因特網(wǎng)628二者都使用承載數(shù)字?jǐn)?shù)據(jù)流的電信號、電磁信號或光信號。通過各種網(wǎng)絡(luò)的信號以及在網(wǎng)絡(luò)鏈路620上并且通過通信接口618的信號是傳輸介質(zhì)的示例形式,其中這些信號把數(shù)字?jǐn)?shù)據(jù)承載到計(jì)算機(jī)系統(tǒng)600或者承載來自計(jì)算機(jī)系統(tǒng)600的數(shù)字?jǐn)?shù)據(jù)。
計(jì)算機(jī)系統(tǒng)600可以通過(一個(gè)或多個(gè))網(wǎng)絡(luò)、網(wǎng)絡(luò)鏈路620和通信接口618發(fā)送消息和接收包括程序代碼的數(shù)據(jù)。在因特網(wǎng)示例中,服務(wù)器630可以通過因特網(wǎng)628、ISP 626、局域網(wǎng)622和通信接口618發(fā)送對應(yīng)用的請求代碼。
接收到的代碼可以由處理器604在它被接收到時(shí)執(zhí)行、和/或被存儲在存儲設(shè)備610或其它非易失性存儲裝置中以供以后執(zhí)行。
如本文所使用的,術(shù)語“第一”、“第二”、“某些”和“特定的”被用作命名約定,以將查詢、計(jì)劃、表示、步驟、對象、設(shè)備或其它項(xiàng)彼此區(qū)分,使得這些項(xiàng)可以在它們被引入之后被引用。除非本文另有指定,否則這些術(shù)語的使用并不暗示所引用的項(xiàng)的順序、時(shí)機(jī)或任何其它特點(diǎn)。
5.0擴(kuò)展和可替代物
在前面的說明書中,參考依實(shí)施方式不同的許多具體細(xì)節(jié)描述了本發(fā)明的實(shí)施例。因此,什么是本發(fā)明以及申請人旨在將什么作為本發(fā)明的唯一且排他的指示物是以權(quán)利要求發(fā)布的具體形式從本申請發(fā)布的一組權(quán)利要求,包括任何后續(xù)補(bǔ)正。用于這些權(quán)利要求中包含的術(shù)語的本文明確闡述的任何定義將決定權(quán)利要求中使用的這些術(shù)語的含義。因此,沒有在權(quán)利要求中明確陳述的限制、元素、性質(zhì)、特征、優(yōu)點(diǎn)或?qū)傩圆粦?yīng)當(dāng)以任何方式限制這些權(quán)利要求的范圍。因而,說明書和附圖應(yīng)當(dāng)在說明性的意義上而不是限制性的意義上被考慮。
6.0附加公開
本文所描述的主題的方面在以下編號的條款中闡述:
1、一種方法,包括:識別創(chuàng)建變量句柄實(shí)例的第一指令,該第一指令包括識別接收者的類型以及由接收者保持的該變量句柄實(shí)例被配置為提供對其的訪問的變量的聲明信息;響應(yīng)于第一指令,并且基于聲明信息,執(zhí)行一個(gè)或多個(gè)檢查,以確定對變量的訪問是否是許可的;響應(yīng)于確定對變量的訪問是許可的,創(chuàng)建變量句柄實(shí)例,該變量句柄實(shí)例包括被配置為對變量的存儲器位置執(zhí)行受約束操作的一個(gè)或多個(gè)約束函數(shù);識別指定對該變量句柄實(shí)例的一個(gè)或多個(gè)受約束函數(shù)中的特定受約束函數(shù)的調(diào)用的第二指令,其中第二指令指示接收者;以及識別存儲變量的特定存儲器位置并且使該特定受約束函數(shù)相對于該特定存儲器位置被執(zhí)行。
2、如條款1所述的方法,其中接收者是類并且變量是由該類保持的靜態(tài)字段、接收者是類實(shí)例并且變量是由該類實(shí)例保持的字段、接收者是數(shù)組并且變量是該數(shù)組的元素、或者接收者是對表示變量的堆外或直接存儲器位置的引用。
3、如條款1-2中的任何一個(gè)條款所述的方法,其中第一指令調(diào)用查找函數(shù),該查找函數(shù)使變量句柄實(shí)例存儲變量在接收者內(nèi)的偏移,并且將變量句柄實(shí)例的一個(gè)或多個(gè)受約束函數(shù)綁定到識別接收者的類型和變量的類型的一個(gè)或多個(gè)描述符。
4、如條款1-3中的任何一個(gè)條款所述的方法,其中該方法由管理第一存儲器區(qū)域的虛擬機(jī)執(zhí)行,并且變量的實(shí)例被存儲在由虛擬機(jī)管理的第一存儲器區(qū)域外部的第二存儲器區(qū)域中。
5、如條款1-4中的任何一個(gè)條款所述的方法,其中第一指令從特定類導(dǎo)出,并且確定對由接收者保持的變量的訪問是否是許可的是基于該特定類是否具有訪問由接收者保持的變量的許可。
6、如條款1-5中的任何一個(gè)條款所述的方法,還包括響應(yīng)于確定對由接收者保持的變量的訪問是不許可的來生成訪問錯(cuò)誤。
7、如條款1-6中的任何一個(gè)條款所述的方法,其中該方法由虛擬機(jī)執(zhí)行,并且虛擬機(jī)通過生成由虛擬機(jī)正在其上執(zhí)行的硬件支持的機(jī)器代碼指令來執(zhí)行受約束的操作。
8、如條款7所述的方法,其中受約束操作包括不由硬件本身支持的至少一個(gè)受約束操作,并且還包括使用由硬件支持的機(jī)器代碼指令的集合來合成受約束操作。
9、如條款1-8中的任何一個(gè)條款所述的方法,其中受約束操作執(zhí)行以下操作中的一個(gè)或多個(gè):放寬的操作、易失性操作、懶惰的操作、比較和設(shè)置操作或者獲得和設(shè)置操作。
10、如條款1-9中的任何一個(gè)條款所述的方法,其中第二指令包括用于特定受約束函數(shù)的一個(gè)或多個(gè)參數(shù)。
11、如條款10所述的方法,其中該一個(gè)或多個(gè)參數(shù)包括以下各項(xiàng)中的一個(gè)或多個(gè):變量的實(shí)例所位于的到接收者的實(shí)例的數(shù)組索引或者要存儲到變量的實(shí)例的特定存儲器位置的值。
12、如條款1-11中的任何一個(gè)條款所述的方法,還包括:執(zhí)行檢查,以確保由第二指令指定的接收者的實(shí)例與由聲明信息識別的接收者的類型匹配。
13、如條款1-12中的任何一個(gè)條款所述的方法,其中使特定受約束函數(shù)相對于特定存儲器位置被執(zhí)行包括經(jīng)由一條或多條硬件指令調(diào)用與任意存儲器位置交互的函數(shù)。
14、如條款1-13中的任何一個(gè)條款所述的方法,其中與任意存儲器位置交互的函數(shù)不執(zhí)行類型檢查。
15、如條款1-14中的任何一個(gè)條款所述的方法,其中變量句柄實(shí)例的一個(gè)或多個(gè)受約束函數(shù)包括對應(yīng)于一個(gè)或多個(gè)原子算術(shù)運(yùn)算的函數(shù)。
16、如條款1-15中的任何一個(gè)條款所述的方法,其中變量句柄實(shí)例從擴(kuò)展抽象變量句柄類的變量句柄子類派生,該抽象變量句柄類為一個(gè)或多個(gè)受約束函數(shù)提供一個(gè)或多個(gè)接口,并且變量句柄子類對特定訪問類型實(shí)現(xiàn)一個(gè)或多個(gè)受約束函數(shù)。
17、如條款1-16中的任何一個(gè)條款所述的方法,還包括:響應(yīng)于確定由第二指令調(diào)用的特定受約束函數(shù)是簽名多態(tài)的,基于表示對特定受約束函數(shù)的調(diào)用的引用的描述符來自動(dòng)啟用預(yù)定義函數(shù),其中該預(yù)定義函數(shù)執(zhí)行類型檢查并且啟用執(zhí)行該特定受約束函數(shù)的變量句柄的函數(shù)。
18、如條款17所述的方法,其中確定特定受約束函數(shù)是簽名多態(tài)的通過檢查抽象變量句柄類的運(yùn)行時(shí)表示中的與該特定受約束函數(shù)相關(guān)聯(lián)的一個(gè)或多個(gè)標(biāo)志來執(zhí)行。
19、如條款17-18中的任何一個(gè)條款所述的方法,其中作為基于聲明信息創(chuàng)建變量句柄實(shí)例的結(jié)果,類型檢查通過將一個(gè)或多個(gè)參數(shù)和對特定受約束函數(shù)的調(diào)用的返回類型與和用于變量句柄實(shí)例的特定受約束函數(shù)相關(guān)聯(lián)的描述符進(jìn)行比較來執(zhí)行。
20、如條款17-19中的任何一個(gè)條款所述的方法,其中啟用變量句柄實(shí)例的特定受約束函數(shù)至少包括將對常量池中的特定受約束函數(shù)的符號引用解析為表示運(yùn)行時(shí)存儲器中的該特定受約束函數(shù)的位置的具體存儲器引用。
21、如條款20所述的方法,還包括在運(yùn)行時(shí)存儲器中高速緩存特定受約束函數(shù)的位置,以便在對該變量句柄實(shí)例的該特定受約束函數(shù)的未來調(diào)用期間重用。
22、如條款17-20中的任何一個(gè)條款所述的方法,其中變量句柄實(shí)例的特定受約束函數(shù)至少執(zhí)行一個(gè)或多個(gè)安全檢查,該一個(gè)或多個(gè)安全檢查如果成功,則使得訪問該特定存儲器位置并且執(zhí)行與該特定受約束函數(shù)對應(yīng)的受約束操作的不安全函數(shù)被啟用。
23、一種方法,包括:生成被配置為通過一個(gè)或多個(gè)存儲器防護(hù)操作提供對存儲器的安全訪問的對象;通過該對象,接收指示存儲器位置并且指定該一個(gè)或多個(gè)存儲器防護(hù)操作中的要相對于該存儲器位置執(zhí)行的特定存儲器防護(hù)操作的調(diào)用;使得該特定存儲器防護(hù)操作相對于該存儲器位置被執(zhí)行;以及其中該方法由一個(gè)或多個(gè)處理器執(zhí)行。
24、如條款23所述的方法,其中對象與定義一個(gè)或多個(gè)存儲器防護(hù)操作的類相關(guān),但是不包括用于多個(gè)不同變量種類中的任何特定變量種類的一個(gè)或多個(gè)存儲器防護(hù)操作的實(shí)現(xiàn)。
25、如條款24所述的方法,其中所述多個(gè)不同變量種類包括以下的一個(gè)或多個(gè):實(shí)例字段變量、靜態(tài)字段變量、數(shù)組元素變量或堆外變量。
26、如條款24-25中的任何一個(gè)條款所述的方法,其中對象是從該類擴(kuò)展并且相對于多個(gè)不同變量種類中的特定變量種類實(shí)現(xiàn)一個(gè)或多個(gè)存儲器防護(hù)操作的子類的實(shí)例。
27、如條款24-25中的任何一個(gè)條款所述的方法,還包括:生成作為該類的實(shí)例的第二對象;通過該第二對象,接收指示第二存儲器位置并且指定一個(gè)或多個(gè)存儲器防護(hù)操作中的要相對于第二存儲器位置執(zhí)行的第二特定存儲器防護(hù)操作的第二調(diào)用,其中存儲器中的存儲器位置表示第一變量類型并且第二存儲器位置表示第二變量類型,其中第一變量類型不同于第二變量類型;以及使得特定存儲器防護(hù)操作相對于第二存儲器位置被執(zhí)行。
28、如條款27所述的方法,其中第一變量類型和第二變量類型是以下的一個(gè)或多個(gè):整型、長整型、浮點(diǎn)型、雙精度型或?qū)ο笠谩?/p>
29、如條款23-28中的任何一個(gè)條款所述的方法,其中生成對象將該對象綁定到第一變量類型,其中調(diào)用指定訪問存儲器位置以基于第二變量類型執(zhí)行特定存儲器防護(hù)操作,并且該方法還包括:確定第一變量類型是否與第二變量類型匹配,其中響應(yīng)于確定第一變量類型與第二變量類型匹配,使得該特定存儲器防護(hù)操作相對于該存儲器位置被執(zhí)行;響應(yīng)于確定第一變量類型不匹配第二變量類型,執(zhí)行以下操作中的一個(gè)或多個(gè):生成錯(cuò)誤,或者使第一變量類型符合第二變量類型,然后使得該特定存儲器防護(hù)操作相對于該存儲器位置被執(zhí)行。
30、如條款23-29中的任何一個(gè)條款所述的方法,其中生成對象包括執(zhí)行檢查以確定對位置的訪問是否是許可的,并且該方法還包括通過該對象接收多個(gè)調(diào)用以在不用重復(fù)檢查的情況下執(zhí)行存儲器防護(hù)操作。
31、如條款23-30中的任何一個(gè)條款所述的方法,其中特定存儲器防護(hù)操作通過除了向存儲器位置發(fā)出一條或多條加載或存儲指令之外還向存儲器位置至少發(fā)出由一個(gè)或多個(gè)處理器本機(jī)支持的一條或多條存儲器屏障指令來執(zhí)行。
32、如條款31所述的方法,其中特定存儲器防護(hù)操作通過組合由一個(gè)或多個(gè)處理器本機(jī)支持的一條或多條存儲器屏障指令中的兩條或更多條存儲器屏障指令來執(zhí)行。
33、如條款23-32中的任何一個(gè)條款所述的方法,還包括:通過該對象接收第二調(diào)用,該第二調(diào)用指定與要相對于該存儲器位置執(zhí)行的、不同于該特定存儲器防護(hù)操作的第二存儲器防護(hù)操作;以及使得第二存儲器防護(hù)操作相對于該存儲器位置被執(zhí)行。
34、如條款23-33中的任何一個(gè)條款所述的方法,其中使得特定存儲器防護(hù)操作被執(zhí)行涉及啟用與執(zhí)行一個(gè)或多個(gè)安全檢查的對象相關(guān)聯(lián)的代碼,其中該一個(gè)或多個(gè)安全檢查包括以下的一個(gè)或多個(gè):確定傳遞到調(diào)用中的一個(gè)或多個(gè)參數(shù)匹配對象預(yù)期的一組參數(shù)、確定對傳遞到調(diào)用中的存儲器位置的引用是否為空、或者確定傳遞到調(diào)用中的數(shù)組索引是否有效。
35、如條款23-34中的任何一個(gè)條款所述的方法,其中存儲器防護(hù)操作包括用于一個(gè)或多個(gè)訪問模式的操作,其中該一個(gè)或多個(gè)訪問模式包括以下的一個(gè)或多個(gè):放寬的讀取、放寬的寫入、易失性讀取、易失性寫入、原子讀取、原子寫入或原子更新。
36、一個(gè)或多個(gè)非暫態(tài)計(jì)算機(jī)可讀介質(zhì),該一個(gè)或多個(gè)非暫態(tài)計(jì)算機(jī)可讀介質(zhì)存儲指令,當(dāng)該指令由一個(gè)或多個(gè)計(jì)算設(shè)備執(zhí)行時(shí),使得如條款1-35中的任何一個(gè)條款所述的方法被執(zhí)行。
37、一種系統(tǒng),該系統(tǒng)包括一個(gè)或多個(gè)計(jì)算設(shè)備,該一個(gè)或多個(gè)計(jì)算設(shè)備包括至少部分地由計(jì)算硬件實(shí)現(xiàn)的、被配置為實(shí)現(xiàn)如條款1-35中的任何一個(gè)條款所述的方法的步驟的組件。
7.0第二附加公開
下文公開本發(fā)明的附加實(shí)施例。此外,本節(jié)包括從針對Java虛擬機(jī)(JVM)編寫的各種規(guī)范拉取的附加描述,在一些實(shí)施例中,JVM被用來實(shí)現(xiàn)本文所討論的技術(shù)。然而,不要求實(shí)施例使用本節(jié)中討論的特征中的任何特征或所有特征。
本文描述了用于通過使用支持各種不同存儲器訪問模式的“句柄”來提供對存儲器位置的安全和高效的外來(exotic)訪問的技術(shù)。句柄提供了關(guān)于對這些存儲器位置的操作的若干級別的抽象。這些抽象中的一個(gè)抽象涉及存儲器位置本身的性質(zhì),因?yàn)榇鎯ζ魑恢每梢源鎯Ω鞣N類型的字段或其它數(shù)據(jù)結(jié)構(gòu)中的任何類型的字段或其它數(shù)據(jù)結(jié)構(gòu)。存儲器位置可以是由語言運(yùn)行環(huán)境(language runtime)(諸如虛擬機(jī)或解釋器)管理的位置和/或“不受管理”的位置,因?yàn)樗鼈冇烧Z言運(yùn)行環(huán)境之外的存儲器管理器(例如,由操作系統(tǒng)分配給不在語言運(yùn)行環(huán)境內(nèi)運(yùn)行的應(yīng)用的存儲器)管理。這些抽象中的另一個(gè)抽象涉及句柄支持的存儲器訪問模式,因?yàn)榫浔粚⒋鎯ζ魑恢面i定到具體的存儲器訪問模式(如通常在變量的情況下發(fā)生的),而是可以提供由解釋器允許并且由底層硬件支持的任何存儲器訪問模式。
根據(jù)實(shí)施例,用于利用這些句柄的方法包括生成被配置為通過不同的存儲器防護(hù)操作提供對存儲器的安全訪問的對象結(jié)構(gòu)。例如,對象結(jié)構(gòu)可以是(如本文所描述的)VarHandle類或者被抽象地定義以提供對任意類型的數(shù)據(jù)結(jié)構(gòu)的任意訪問模式的任何其它合適的類的實(shí)例。隨后描述存儲器防護(hù)操作的示例。雖然為了方便起見在本文中句柄被描述為對象結(jié)構(gòu),但是將理解的是,取決于編程語言,句柄可以是任何合適的構(gòu)造,并且不一定需要被嚴(yán)格限制為對象或甚至面向?qū)ο蟮臉?gòu)造。
方法還包括通過對象結(jié)構(gòu)接收指示存儲器中的位置并且指定要相對于所指示的存儲器中的位置執(zhí)行存儲器防護(hù)操作中的哪一個(gè)存儲器防護(hù)操作的調(diào)用(諸如對由它的類定義的方法的調(diào)用)。調(diào)用可以使用各種約定來指示位置,諸如通過引用由實(shí)現(xiàn)該方法的語言運(yùn)行環(huán)境(例如,虛擬機(jī)或解釋器)管理的位置、通過指定另一對象的先前聲明的和/或?qū)嵗淖侄?、通過引用靜態(tài)字段、通過引用數(shù)組元素或者通過引用不由語言運(yùn)行環(huán)境管理的位置,諸如尚未分配給語言運(yùn)行環(huán)境的位置或者由不同于語言運(yùn)行環(huán)境或獨(dú)立于語言運(yùn)行環(huán)境的過程分配和/或管理的位置(例如,“堆外”位置)。
方法還包括使得指定的存儲器防護(hù)操作相對于所指示的位置被執(zhí)行。例如,由對象結(jié)構(gòu)的類定義和/或以其它方式與對象結(jié)構(gòu)的類相關(guān)聯(lián)的邏輯可以被執(zhí)行,以執(zhí)行存儲器防護(hù)操作,和/或向執(zhí)行硬件發(fā)出使得該硬件執(zhí)行存儲器防護(hù)操作的低級指令。
如本文所使用的,存儲器防護(hù)操作(也被稱為存儲器屏障、存儲器圍欄或圍欄指令)是使得中央處理單元(CPU)或編譯器對在屏障指令之前和之后發(fā)出的存儲器操作實(shí)施排序約束的一種類型的屏障指令。存儲器屏障可以是必要的,因?yàn)樵S多現(xiàn)代CPU采用可能導(dǎo)致無序執(zhí)行的性能優(yōu)化。存儲器操作(加載和存儲)的這種重新排序通常在單個(gè)執(zhí)行線程內(nèi)不被注意,但是除非被仔細(xì)控制,否則可以造成并發(fā)程序和設(shè)備驅(qū)動(dòng)程序中的不可預(yù)測的行為。順序約束的確切本質(zhì)是依賴硬件的并且由架構(gòu)的存儲器排序模型定義。一些架構(gòu)提供了用于實(shí)施不同排序約束的多個(gè)屏障。
在實(shí)施例中,生成對象結(jié)構(gòu)包括執(zhí)行檢查,以確定對位置和/或與該位置相關(guān)聯(lián)的數(shù)據(jù)類型的訪問在當(dāng)前執(zhí)行上下文內(nèi)是否是許可的。方法還包括通過對象結(jié)構(gòu)接收多個(gè)調(diào)用,以在不重復(fù)檢查的情況下執(zhí)行存儲器防護(hù)操作。
在實(shí)施例中,方法由基于例如較低級代碼(諸如Java字節(jié)碼或從人類可讀源代碼編譯的任何其它代碼)中的指令的語言運(yùn)行環(huán)境執(zhí)行。方法還包括基于確定由在其上實(shí)現(xiàn)語言運(yùn)行環(huán)境的硬件本機(jī)支持的機(jī)器存儲器架構(gòu)和/或指令集來確定如何執(zhí)行指定的存儲器防護(hù)操作。方法還包括選擇指令集中執(zhí)行指定的存儲器防護(hù)操作的具體存儲器防護(hù)指令。
在實(shí)施例中,方法還包括生成作為類的實(shí)例的多個(gè)對象結(jié)構(gòu)。方法還包括通過這多個(gè)對象結(jié)構(gòu)接收調(diào)用以相對于不同類型的數(shù)據(jù)結(jié)構(gòu)(例如,不同的原語類型、不同的值類型等)執(zhí)行存儲器防護(hù)操作。調(diào)用指定數(shù)據(jù)結(jié)構(gòu)和/或數(shù)據(jù)結(jié)構(gòu)的類型。方法還包括基于類中的邏輯或與類相關(guān)聯(lián)的邏輯來使得指定的存儲器防護(hù)操作被執(zhí)行。從而,單個(gè)類提供對各種類型的數(shù)據(jù)結(jié)構(gòu)的抽象句柄。
在實(shí)施例中,方法還包括通過對象結(jié)構(gòu)接收指定要相對于同一存儲器位置執(zhí)行的不同存儲器防護(hù)操作的多個(gè)調(diào)用,不同的存儲器防護(hù)操作包括具有不同存儲器訪問模式(例如,易失性訪問、懶惰的訪問、放寬的訪問、正常訪問等的任何組合)的至少兩個(gè)操作。從而,單個(gè)類為同一存儲器位置提供多個(gè)存儲器訪問模式。
根據(jù)實(shí)施例,用于利用所描述的句柄的另一方法(在一些情況下,但不一定在所有情況下,其構(gòu)成先前描述的方法的具體實(shí)例)包括識別創(chuàng)建變量句柄對象的第一指令。這樣的指令的一個(gè)示例可以是變量聲明,該變量聲明包括對“查找”方法(諸如類似于本文所描述的VarHandles.Lookup之類的方法)的調(diào)用或者對例如Java字節(jié)碼中的這樣的方法的等價(jià)啟用的調(diào)用。第一指令包括至少指示變量句柄對象被配置為提供對其的訪問的結(jié)構(gòu)類型的配置信息。例如,配置信息可以指定以下各項(xiàng)中的一個(gè)或多個(gè)的任何合適的組合:對象類或數(shù)據(jù)結(jié)構(gòu)類型、與為對象類或數(shù)據(jù)結(jié)構(gòu)類型定義的字段對應(yīng)的字段名稱,和/或字段類或類型。作為另一個(gè)示例,配置信息可以引用不受管理的存儲器位置,該不受管理的存儲器位置包括尚未分配給正在執(zhí)行第一指令的語言運(yùn)行環(huán)境(諸如虛擬機(jī))的存儲器位置。
方法還包括,響應(yīng)于第一指令并且基于配置信息來執(zhí)行一個(gè)或多個(gè)檢查,以確定對結(jié)構(gòu)類型的訪問是否是許可的。例如,可以執(zhí)行訪問檢查,以確定與結(jié)構(gòu)類型相關(guān)聯(lián)的各種規(guī)則或聲明(諸如“私有”或“公有”聲明)是否許可從當(dāng)前執(zhí)行上下文(諸如在其中創(chuàng)建變量句柄對象的函數(shù)或方法)內(nèi)訪問結(jié)構(gòu)類型的實(shí)例。可以被執(zhí)行的另一示例檢查是確定結(jié)構(gòu)類型是否實(shí)際存在。可選地,方法還包括如果檢查中的一個(gè)或多個(gè)檢查失敗則拋出錯(cuò)誤。例如,可以拋出指示不能找到被引用的結(jié)構(gòu)類型的錯(cuò)誤,或者根據(jù)與結(jié)構(gòu)類型相關(guān)聯(lián)的安全規(guī)則對該結(jié)構(gòu)類型的訪問將不被許可,諸如如果結(jié)構(gòu)類型是私有的話。
方法還包括:如果對結(jié)構(gòu)類型的訪問是許可的,則創(chuàng)建變量句柄對象。該變量句柄對象包括被配置為對結(jié)構(gòu)類型的存儲器位置執(zhí)行原子操作的方法。這樣的原子操作的示例可以包括例如存儲器防護(hù)操作或直接與可以由其上實(shí)現(xiàn)語言運(yùn)行環(huán)境的硬件支持的低級硬件指令對應(yīng)的操作??蛇x地,操作可以包括由語言運(yùn)行環(huán)境被配置為對結(jié)構(gòu)類型使用的默認(rèn)存儲器防護(hù)模式不支持的至少一個(gè)存儲器防護(hù)操作。例如,存儲器訪問可以是由字段如何被聲明而暗示的模式。即,如果字段類型被聲明為“volatile(易失性)”,則在操作的正常過程期間語言運(yùn)行環(huán)境將僅能夠使用易失性存儲器訪問模式來執(zhí)行對該類型的字段進(jìn)行讀取或?qū)懭氲闹噶?。相反,變量句柄對象將包括能夠訪問抽象地存儲該字段的存儲器位置的方法,而不限于任何暗示的存儲器訪問模式。隨后將更詳細(xì)地描述存儲器防護(hù)操作和存儲器訪問模式。
方法還包括識別調(diào)用變量句柄對象的方法中的一個(gè)方法的第二指令。本文描述這樣的第二指令的示例,諸如“get(獲得)”、“setVolatile(易失性設(shè)置)”等等。第二指令引用數(shù)據(jù)結(jié)構(gòu)類型的實(shí)例或包括數(shù)據(jù)結(jié)構(gòu)類型的對象的實(shí)例。例如,第二指令可以是包含對象、數(shù)組索引或字段本身的任何合適的組合。第二結(jié)構(gòu)可選地包括用于要被執(zhí)行的對應(yīng)原子操作的一個(gè)或多個(gè)參數(shù),諸如用于要讀取/寫入的存儲器位置的數(shù)組索引、寫入存儲器位置的值等等。在實(shí)施例中,第一指令和第二指令可以是同一指令的一部分,或者可以是分開的指令。
方法還包括識別由所引用的實(shí)例指示的存儲器位置。例如,可以提供各種查找機(jī)制以在存儲器中定位被引用的字段,或者定位包括所指示的結(jié)構(gòu)類型的字段的所引用對象實(shí)例的開始位置,以及確定該字段離該開始位置的偏移。可選地,可以執(zhí)行檢查,以確保該字段具有與創(chuàng)建變量句柄對象時(shí)所指示的相同的結(jié)構(gòu)類型。
方法還包括使得對應(yīng)的原子操作相對于存儲器位置被執(zhí)行。例如,語言運(yùn)行環(huán)境可以通過確定實(shí)現(xiàn)原子操作的具體的低級硬件指令(如果可用的話)并且向硬件發(fā)送該指令來使得對應(yīng)的原子操作被執(zhí)行。作為另一個(gè)示例,在Java編程語言中,語言運(yùn)行環(huán)境可以執(zhí)行“不安全”方法,以直接訪問存儲器位置。
在實(shí)施例中,變量句柄對象的方法包括與以下各項(xiàng)中的一個(gè)或多個(gè)對應(yīng)的方法:放寬的設(shè)置、放寬的獲得、易失性獲得、易失性設(shè)置、獲取獲得、釋放設(shè)置、比較和設(shè)置、獲得和設(shè)置、原子加法和/或原子減法。
在實(shí)施例中,存儲器位置是在分配給正在執(zhí)行第一指令和第二指令的語言運(yùn)行環(huán)境的存儲器的外部的不受管理的存儲器位置。在實(shí)施例中,存儲器位置是受管理的存儲器位置,其可以以各種形式是靜態(tài)字段、實(shí)例字段、數(shù)組、數(shù)組元素或其它種類的基于存儲器的變量。
在實(shí)施例中,可以提供定義用于訪問方法的接口和用于實(shí)現(xiàn)方法的邏輯的類類型??梢詾樗凶兞烤浔鷮ο筇峁﹩蝹€(gè)類類型,而不管它們對其提供訪問的數(shù)據(jù)結(jié)構(gòu)的類型?;蛘?,可以為靜態(tài)文件和/或?qū)嵗募?、?shù)組、數(shù)組元素、數(shù)字原語(primitive)和/或其它類型的變量提供不同的類類型。
在實(shí)施例中,變量句柄對象的方法中的一個(gè)或多個(gè)方法被定義為具有多態(tài)簽名,如在其它節(jié)中所描述的。在實(shí)施例中,變量句柄對象的方法中的一個(gè)或多個(gè)方法被定義為具有通用簽名,并且該方法還包括對于例如編譯后的Java字節(jié)碼中的變量句柄對象的方法中的一些或所有方法在調(diào)用點(diǎn)處具體化通用簽名。
在實(shí)施例中,方法還包括識別參考潛在不同的對象或數(shù)據(jù)結(jié)構(gòu)實(shí)例調(diào)用變量句柄對象的不同方法的多條指令,以及響應(yīng)于該多條指令使得取決于所引用的實(shí)例不同的原子操作相對于同一存儲器位置和/或相對于不同的存儲器位置被執(zhí)行。
在實(shí)施例中,方法還包括部分地基于該一個(gè)或多個(gè)檢查來確定由變量句柄對象支持的原子操作。例如,如果數(shù)據(jù)結(jié)構(gòu)類型已被聲明為“final(最終的)”,則將不支持設(shè)置操作,并且可能(甚至潛在地可能)更新存儲器的任何操作將拋出異常。
在其它方面,公開了用于實(shí)現(xiàn)本文所描述的技術(shù)的計(jì)算設(shè)備的系統(tǒng)和計(jì)算機(jī)可讀介質(zhì)。
invokedynamic指令
invokedynamic指令簡化并且潛在地改進(jìn)用于JVM上的動(dòng)態(tài)語言的編譯器和運(yùn)行時(shí)系統(tǒng)的實(shí)現(xiàn)。invokedynamic指令通過允許語言實(shí)現(xiàn)者定義定制的鏈接行為來做到這一點(diǎn)。這與其中特定于Java類和接口的鏈接行為由JVM硬連線的其它JVM指令(諸如invokevirtual)形成對比。
invokedynamic指令的每個(gè)實(shí)例被稱為動(dòng)態(tài)調(diào)用點(diǎn)(dynamic call site)。動(dòng)態(tài)調(diào)用點(diǎn)最初處于未鏈接狀態(tài),這意味著不存在要啟用的為調(diào)用點(diǎn)指定的方法。如之前提到的,動(dòng)態(tài)調(diào)用點(diǎn)通過引導(dǎo)(bootstrap)方法被鏈接到方法。動(dòng)態(tài)調(diào)用點(diǎn)的引導(dǎo)方法是編譯器為動(dòng)態(tài)類型語言指定的由JVM調(diào)用一次以鏈接站點(diǎn)的方法。從引導(dǎo)方法返回的對象永久地確定調(diào)用點(diǎn)的行為。
invokedynamic指令包含(以與用于其它啟用指令的格式相同的格式的)常量池索引。該常量池索引引用CONSTANT_InvokeDynamic條目。該條目指定引導(dǎo)方法(CONSTANT_MethodHandle條目)、動(dòng)態(tài)鏈接方法的名稱,以及對動(dòng)態(tài)鏈接方法的調(diào)用的變元類型和返回類型。
以下是invokedynamic指令的示例。在這個(gè)示例中,運(yùn)行時(shí)系統(tǒng)通過使用引導(dǎo)方法Example.mybsm來將由該invokedynamic指令指定的動(dòng)態(tài)調(diào)用點(diǎn)(其為+,加法運(yùn)算符)鏈接到IntegerOps.adder方法。方法adder和mybsm在上面定義:
注意:這些節(jié)中的字節(jié)碼示例使用ASM Java字節(jié)碼操縱和分析框架的語法。
用invokedynamic指令啟用動(dòng)態(tài)鏈接的方法包括以下步驟:
1.定義引導(dǎo)方法
在運(yùn)行時(shí),當(dāng)JVM首次遇到invokedynamic指令時(shí),它調(diào)用引導(dǎo)方法。該方法將由invokedynamic指令指定的名稱與應(yīng)當(dāng)被執(zhí)行的代碼(目標(biāo)方法)鏈接,該代碼由方法句柄引用。如果JVM再次執(zhí)行相同的invokedynamic指令,則它不調(diào)用引導(dǎo)方法;它自動(dòng)調(diào)用鏈接的方法句柄。
引導(dǎo)方法的返回類型可以是java.lang.invoke.CallSite。CallSite對象表示invokedynamic指令和它鏈接到的方法句柄的鏈接狀態(tài)。
引導(dǎo)方法采用三個(gè)或更多個(gè)參數(shù):MethodHandles.Lookup對象,它是用于在invokedynamic指令的上下文中創(chuàng)建方法句柄的工廠;String對象,在動(dòng)態(tài)調(diào)用點(diǎn)中提到的方法名;以及MethodType對象,動(dòng)態(tài)調(diào)用點(diǎn)的解析的類型簽名。
可選地,對invokedynamic指令的一個(gè)或多個(gè)附加靜態(tài)變元。從常量池汲取(draw)的這些變元旨在幫助語言實(shí)現(xiàn)者安全、緊湊地將對引導(dǎo)方法有用的附加元數(shù)據(jù)進(jìn)行編碼。原則上,名稱和確切的變元是冗余的,因?yàn)槊總€(gè)調(diào)用點(diǎn)可以被給予其自己的唯一的引導(dǎo)方法。然而,這樣的做法可能產(chǎn)生大的類文件和常量池。對于引導(dǎo)方法的示例,請參見上文。
2.指定常量池條目
如之前所提到的,invokedynamic指令包含對常量池中具有標(biāo)簽CONSTANT_InvokeDynamic的條目的引用。該條目包含對常量池中其它條目的引用和對屬性的引用。本節(jié)簡要描述由invokedynamic指令使用的常量池條目。對于更多信息,請參見java.lang.invoke包文檔和Java虛擬機(jī)規(guī)范(The Java Virtual Machine Specification)。
示例常量池
以下是來自用于類Example的常量池的摘錄(excerpt),其包含將方法+與Java方法adder鏈接的引導(dǎo)方法Example.mybsm:
在這個(gè)示例中用于invokedynamic指令的常量池條目包含三個(gè)值:
CONSTANT_InvokeDynamic tag
Unsigned short of value 0
Constant pool index#234.
值0是指存儲在BootstrapMethods屬性中的指定符數(shù)組中的第一引導(dǎo)方法指定符。引導(dǎo)方法指定符不在常量池表中;它們包含在這個(gè)分開的指定符數(shù)組中。每個(gè)引導(dǎo)方法指定符包含對作為引導(dǎo)方法本身的CONSTANT_MethodHandle常量池條目的索引。
以下是來自同一常量池的摘錄,其示出了BootstrapMethods屬性,該屬性包含引導(dǎo)方法指定符的數(shù)組:
用于引導(dǎo)方法mybsm方法句柄的常量池條目包含三個(gè)值:
CONSTANT_MethodHandle tag
Unsigned byte of value 6
Constant pool index#232.
值6是子標(biāo)記REF_invokeStatic。
3.使用invokedynamic指令
以下字節(jié)碼使用invokedynamic指令來調(diào)用引導(dǎo)方法mybsm,該引導(dǎo)方法mybsm將動(dòng)態(tài)調(diào)用點(diǎn)(+,加法運(yùn)算符)鏈接到方法adder。這個(gè)示例使用+方法將數(shù)字40和2相加(為了清晰,已插入換行符):
前四條指令將整數(shù)40和2放在棧上,并且以java.lang.Integer包裝器類型將它們裝箱。第五條指令啟用動(dòng)態(tài)方法。這條指令參考具有CONSTANT_InvokeDynamic標(biāo)簽的常量池條目:
這個(gè)條目中的CONSTANT_InvokeDynamic標(biāo)簽之后跟著四個(gè)字節(jié):
前兩個(gè)字節(jié)形成對引用引導(dǎo)方法指定符的CONSTANT_MethodHandle條目的引用:
如之前所提到的,對引導(dǎo)方法指定符的該引用不在常量池表中;它包含在由類文件屬性namedBootstrapMethods定義的分開的數(shù)組中。引導(dǎo)方法指定符包含對作為引導(dǎo)方法本身的CONSTANT_MethodHandle常量池條目的索引。
在該CONSTANT_MethodHandle常量池條目之后有三個(gè)字節(jié):第一個(gè)字節(jié)是子標(biāo)簽REF_invokeStatic。這意味著該引導(dǎo)方法將為靜態(tài)方法創(chuàng)建方法句柄;注意到該引導(dǎo)方法正在將動(dòng)態(tài)調(diào)用點(diǎn)與靜態(tài)Java方法adder鏈接。接下來的兩個(gè)字節(jié)形成表示要為其創(chuàng)建方法句柄的方法的CONSTANT_Methodref條目:
在這個(gè)示例中,引導(dǎo)方法的完全限定名稱是Example.mybsm;變元類型為MethodHandles.Lookup、String和MethodType;而返回類型是CallSite。
接下來的兩個(gè)字節(jié)形成對CONSTANT_NameAndType條目的引用:
該常量池條目指定方法名(+)、變元類型(兩個(gè)Integer實(shí)例)以及動(dòng)態(tài)調(diào)用點(diǎn)的返回類型(Integer)。
在這個(gè)示例中,給出具有箱化的整數(shù)值的動(dòng)態(tài)調(diào)用點(diǎn),其與最終目標(biāo)(adder方法)的類型完全匹配。實(shí)際上,變元和返回類型不需要確切地匹配。例如,invokedynamic指令可以在JVM棧上傳遞其操作數(shù)中的任一個(gè)或兩個(gè)作為原始整型值。任一個(gè)或兩個(gè)操作數(shù)還可以是未類型化(untyped)的對象值。invokedynamic指令還可以接收其結(jié)果作為原始整型值或未類型化的對象值。在任何情況下,mybsm的dynMethodType變元都將準(zhǔn)確地描述invokedynamic指令所需的方法類型。
獨(dú)立地,adder方法還可以被賦予原始的或未類型化的變元或返回值。引導(dǎo)方法負(fù)責(zé)彌補(bǔ)theDynMethodType和adder方法的類型之間的任何差異。如代碼中所示,這容易用對目標(biāo)方法的asType調(diào)用來完成。
解構(gòu)MethodHandle
本節(jié)使用設(shè)置非靜態(tài)字段的值的具體示例來解構(gòu)(deconstruct)MethodHandle的確切啟用如何被編譯、鏈接和執(zhí)行。通過編譯和執(zhí)行基于MethodHandle的jmh微基準(zhǔn)來獲得字節(jié)碼和內(nèi)聯(lián)蹤跡的示例。
盡管名稱如此,但是Methodhandle可以引用類的底層靜態(tài)字段或?qū)嵗侄巍T贠penJDK實(shí)現(xiàn)中,在執(zhí)行了適當(dāng)?shù)陌踩珯z查之后,對這樣的句柄的啟用產(chǎn)生對sun.misc.Unsafe上的方法的對應(yīng)調(diào)用。例如,如果字段是被標(biāo)記為易失性的引用類型(非原語類型),則Unsafe.putObjectVolatile方法將被啟用。
如果對MethodHandle的這樣的引用被保持在靜態(tài)最終字段中,則運(yùn)行環(huán)境應(yīng)當(dāng)能夠?qū)υ撘玫膯⒂煤彤?dāng)內(nèi)聯(lián)發(fā)生時(shí)它所保持的內(nèi)容進(jìn)行常量折疊。在這樣的情況下,可能令人驚訝的是,與對Unsafe或get/putfield字節(jié)碼指令直接啟用方法相比,生成的機(jī)器代碼可以具有競爭力。
通過分別對Unsafe、putObject、putOrderedObject和compareAndSwapObject啟用適當(dāng)?shù)姆椒?,擴(kuò)展MethodHandle實(shí)現(xiàn)以支持用于放寬的、懶惰的以及比較和設(shè)置原子操作的句柄是直接的。
給出對類(例如,Receiver)上的非靜態(tài)字段(例如,具有類型Value的)“vfield”的寫訪問的MethodHandle可以如下獲得:
MethodHandles.Lookup lookup=...
MethodHandler setterOfValueOnReceiver=
lookup.findSetter(Receiver.class,"vfield",Value.class);
然后,該MethodHandle的確切啟用將設(shè)置Receiver的實(shí)例上的字段“vfield”的值:
Receiver r=...
Value v=...
setterOfValueOnReceiver.invokeExact(r,v);
注意到接收者實(shí)例作為第一參數(shù)被傳遞給invokeExact方法。接收者提供從其訪問它保持的字段“vfield”的值的基本位置(因?yàn)镴ava中直接的l值以及通過字段或數(shù)組元素的引用的傳遞沒有被支持,所以一對接收者和值是必要的)。
MethodHandle.invokeExact方法被聲明為多態(tài)簽名方法:
public final native@PolymorphicSignature Object invokeExact(Object...args)throws Throwable;
當(dāng)javac將啟用編譯成簽名多態(tài)方法時(shí),它使用符號類型描述符作為方法簽名,該符號類型描述符從調(diào)用者的實(shí)際參數(shù)和返回類型而不是從方法聲明的方法簽名導(dǎo)出。
invokevirtual指令的示例如下所示:
13:invokevirtual#12//Methodjava/lang/invoke/MethodHandle.invokeExact:(Lvarmh/VolatileSet AndGetTest$Receiver;Lvarmh/VolatileSetAndGetTest$Value;)V
注意到方法簽名接受兩個(gè)變元,即類Receiver和類Value的實(shí)例,并且返回void,該簽名被稱為符號類型描述符。
當(dāng)句柄被常量折疊時(shí),這樣的啟用的內(nèi)聯(lián)蹤跡如下:
啟用包括兩個(gè)階段。第一階段執(zhí)行高效的運(yùn)行時(shí)安全檢查,以確定在調(diào)用點(diǎn)處編碼的符號類型描述符是否與MethodHandle的方法類型描述符完全匹配。第二階段執(zhí)行寫入訪問,在這種情況下是對易失性字段的寫入訪問。
在動(dòng)態(tài)生成的類上,MethodHandler.invokeExact啟用被內(nèi)部鏈接(參見節(jié)“invokeExact啟用的鏈接”)到靜態(tài)方法invokeExact_MT,該靜態(tài)方法invokeExact_MT的字節(jié)碼是:
用于invocationExact_MT的參數(shù)如下:
MethodHandle實(shí)例、setterOfValueOnReceiver;傳遞給invokeExact方法的參數(shù)(r,v);最后是當(dāng)調(diào)用點(diǎn)鏈接時(shí)附加的調(diào)用點(diǎn)的符號類型描述符。
注意到在該時(shí)間點(diǎn)處,引用參數(shù)類型被擦除為Object,因此聲明該靜態(tài)方法的類可以被共享,以用于利用擦除為相同簽名的不同方法類型描述符的啟用。
該方法首先執(zhí)行方法類型描述符檢查,并且如果該方法類型描述符檢查失敗則拋出異常,否則繼續(xù)是安全的,因?yàn)橐阎{(diào)用點(diǎn)的變元類型和返回類型是正確的并且完全匹配。接下來,利用傳遞給invokeExact方法的相同參數(shù)來啟用MethodHandle實(shí)例上的invokeBasic。
invokeBasic的啟用被內(nèi)部鏈接到對應(yīng)于MethodHandle的編譯的lambda形式的動(dòng)態(tài)生成的類上的靜態(tài)方法putObjectVolatileFieldCast。MethodHandle的LambdaForm的vmentry字段是表征方法putObjectVolatileFieldCast的MemberName,方法putObjectVolatileFieldCast的字節(jié)碼是:
第一個(gè)參數(shù)是MethodHandle實(shí)例并且后續(xù)參數(shù)是傳遞給invokeExact方法的那些參數(shù)(r,v)。MethodHandle實(shí)例是保持要在這個(gè)方法結(jié)束時(shí)與Unsafe.putObjectVolatile的啟用一起使用的字段偏移的直接句柄。在該啟用之前:執(zhí)行安全檢查,以確保接收者實(shí)例不為空;并且執(zhí)行值實(shí)例到值(字段)類型的實(shí)例的轉(zhuǎn)換檢查,以確保運(yùn)行時(shí)編譯器具有足夠的信息來執(zhí)行類型剖析。注意到這不是類型安全所必需的,因?yàn)檫@樣的安全檢查已經(jīng)由invokeExact_MT方法執(zhí)行;觀察接收者實(shí)例的類型不需要轉(zhuǎn)換到接收者類型的實(shí)例。
invokeExact啟用的鏈接
MethodHandler.invokeExact的啟用經(jīng)由對返回表征要被鏈接到的Java方法的MemberName的Java方法的來自VM的向上調(diào)用被內(nèi)部鏈接。這種對于VM來說靜態(tài)已知的被向上調(diào)用的Java方法是MethodHandleNatives.linkMethod:
static MemberName linkMethod(Class<?>callerClass,int refKind,
Class<?>defc,String name,Object type,Object[]appendixResult)
“type”參數(shù)是對應(yīng)于符號類型描述符的MethodType或String的實(shí)例?!癮ppendixResult”被用來返回可選的額外參數(shù),該參數(shù)必須與由MemberName表征的方法的最后一個(gè)參數(shù)匹配,并且將被永久地附加在被鏈接的調(diào)用點(diǎn)處。
在啟用invokeExact時(shí),VM棧已經(jīng)包含三個(gè)值,并且VM將向調(diào)用棧添加一個(gè)附加參數(shù),以使得參數(shù)如下:MethodHandle實(shí)例,setterOfValueOnReceiver;參數(shù)(r,v);最后是作為“appendixResult”的第一個(gè)元素的附加參數(shù),它是調(diào)用點(diǎn)的符號類型描述符。
MethodHandle Class
public abstract class MethodHandle
extends Object
方法句柄是具有變元或返回值的可選變換的、對底層方法、構(gòu)造函數(shù)、字段或類似低級操作的類型化的、可直接執(zhí)行的引用。這些變換是相當(dāng)通用的,并且包括諸如轉(zhuǎn)換、插入、刪除和替換之類的模式。
方法句柄內(nèi)容
方法句柄根據(jù)它們的參數(shù)和返回類型被動(dòng)態(tài)地類型化和強(qiáng)類型化。它們不通過其底層方法的名稱或定義類來區(qū)分。必須使用匹配方法句柄自己的類型描述符的符號類型描述符來啟用方法句柄。
每個(gè)方法句柄經(jīng)由type訪問器報(bào)告其類型描述符。該類型描述符是MethodType對象,它的結(jié)構(gòu)是一系列類,該一系列類中的一個(gè)類是方法的返回類型(如果沒有,則是void.class)。
方法句柄的類型控制它接受的啟用的類型,以及應(yīng)用于它的變換的種類。方法句柄包含一對名為invokeExact和invoke的特殊啟用者(invoker)方法。兩個(gè)啟用者方法都提供對如通過變元和返回值的變換所修改的、方法句柄的底層方法、構(gòu)造函數(shù)、字段或其它操作的直接訪問。兩個(gè)啟用者都接受與方法句柄自己的類型完全匹配的調(diào)用。普通的、不確切的啟用者還接受一系列其它調(diào)用類型。
方法句柄是不可變的并且不具有可見狀態(tài)。當(dāng)然,它們可以被綁定到展現(xiàn)狀態(tài)的底層方法或數(shù)據(jù)。對于Java存儲器模型,任何方法句柄將表現(xiàn)得好像它的(內(nèi)部)字段中的所有字段都是最終變量。這意味著對應(yīng)用可見的任何方法句柄將總是被完全形成。即使方法句柄通過數(shù)據(jù)競爭中的共享變量發(fā)布,也是如此。
方法句柄不能由用戶子類化。實(shí)現(xiàn)可以(或者可能不)創(chuàng)建可以經(jīng)由Object.getClass操作可見的MethodHandle的內(nèi)部子類。程序員不應(yīng)當(dāng)從方法句柄的具體類得出關(guān)于該方法句柄的結(jié)論,因?yàn)榉椒ň浔悓哟谓Y(jié)構(gòu)(如果有的話)可以隨時(shí)間或者跨來自不同供應(yīng)商的實(shí)現(xiàn)而改變。
方法句柄編譯
名為invokeExact或invoke的Java方法調(diào)用表達(dá)式可以啟用來自Java源代碼的方法句柄。從源代碼的角度來看,這些方法可以采用任何變元,并且它們的結(jié)果可以被轉(zhuǎn)換為任何返回類型。形式上,這是通過給予啟用者方法Object返回類型和可變元數(shù)(variable arity)Object變元來完成的,但是它們具有將這種啟用的自由度直接連接到JVM執(zhí)行棧的被稱為簽名多態(tài)性的附加特性。
與虛方法通常一樣,對invokeExact和invoke的源級調(diào)用編譯為invokevirtual指令。較不同尋常的是,編譯器必須記錄實(shí)際的變元類型,并且可以不對變元執(zhí)行方法啟用轉(zhuǎn)換。相反,編譯器必須根據(jù)變元自己的未轉(zhuǎn)換類型把它們推送到棧上。方法句柄對象本身在變元之前被推送到棧上。然后編譯器用描述變元和返回類型的符號類型描述符來調(diào)用方法句柄。
為了發(fā)出完整的符號類型描述符,編譯器還必須確定返回類型。如果存在一個(gè)方法啟用表達(dá)式的話,則這是基于對方法啟用表達(dá)式的轉(zhuǎn)換,否則如果啟用是表達(dá)式則返回類型為Object,否則如果啟用是語句則返回類型為void。轉(zhuǎn)換可以是轉(zhuǎn)換到原語類型(但不是void)。
作為極端情況,未被轉(zhuǎn)換的null變元被給予java.lang.Void的符號類型描述符。與Void類型的歧義是無害的,因?yàn)槌丝找茫淮嬖陬愋蜑閂oid的引用。
方法句柄啟用
第一次執(zhí)行invokevirtual指令時(shí),它通過符號解析指令中的名稱并且驗(yàn)證方法調(diào)用是靜態(tài)合法的而被鏈接。對invokeExact和invoke的調(diào)用也是如此。在這種情況下,為了正確語法而檢查由編譯器發(fā)出的符號類型描述符并且解析它所包含的名稱。因此,只要符號類型描述符在語法上良好形成并且類型存在,則啟用方法句柄的invokevirtual指令將總是鏈接。
當(dāng)在鏈接之后執(zhí)行invokevirtual時(shí),首先由JVM檢查接收方法句柄的類型,以確保其匹配符號類型描述符。如果類型匹配失敗,則意味著調(diào)用者啟用的方法在被啟用的單獨(dú)的方法句柄上不存在。
在invokeExact的情況下,啟用的類型描述符(在解析符號類型名稱之后)必須與接收方法句柄的方法類型完全匹配。在普通的、不確切的invoke的情況下,解析后的類型描述符必須是接收者的asType方法的有效變元。因此,普通的invoke比invokeExact是更被許可的。在類型匹配之后,對invokeExact的調(diào)用直接并且立即啟用方法句柄的底層方法(或其它行為,視情況而定)。
如果由調(diào)用者指定的符號類型描述符與方法句柄自己的類型完全匹配,則對普通的invoke的調(diào)用與對invokeExact的調(diào)用的工作方式相同。如果存在類型不匹配,則invoke嘗試調(diào)整接收方法句柄的類型(就像通過對asType的調(diào)用一樣)以獲得完全可調(diào)用的方法句柄M2。這允許在調(diào)用者和被調(diào)用者之間的更強(qiáng)大的方法類型協(xié)商。(注意:調(diào)整后的方法句柄M2不是可直接觀察的,因此不需要實(shí)現(xiàn)來使其具體化)。
啟用檢查
在典型的程序中,方法句柄類型匹配通常將成功。但是如果匹配失敗,則JVM將直接地(在invokeExact的情況下)或者就像通過對asType的失敗的調(diào)用一樣間接地(在invoke的情況下)拋出WrongMethodTypeException。
因此,在靜態(tài)類型化的程序中可能顯示為鏈接錯(cuò)誤的方法類型不匹配可以在使用方法句柄的程序中顯示為動(dòng)態(tài)的WrongMethodTypeException。
因?yàn)榉椒愋桶盎畹摹盋lass對象,所以方法類型匹配考慮類型名稱和類加載器二者。因此,即使方法句柄M在一個(gè)類加載器L1中創(chuàng)建并且在另一個(gè)L2中使用,方法句柄調(diào)用也是類型安全的,因?yàn)槿缭贚2中解析的調(diào)用者的符號類型描述符與如在L1中解析的原始被調(diào)用者方法的符號類型描述符匹配。L1中的解析在M被創(chuàng)建并且其類型被指派時(shí)發(fā)生,而L2中的解析在invokevirtual指令被鏈接時(shí)發(fā)生。
除了類型描述符的檢查之外,方法句柄調(diào)用其底層方法的能力也是不受限制的。如果方法句柄由可訪問該方法的類對非公有方法形成,則得到的句柄可以在任何地方由接收到對它的引用的任何調(diào)用者使用。
與每次啟用反射方法時(shí)檢查訪問的核心反射(Core Reflection)API不同,當(dāng)創(chuàng)建方法句柄時(shí)執(zhí)行方法句柄訪問檢查。在ldc(參見下文)的情況下,訪問檢查作為鏈接在常量方法句柄底層的常量池條目的一部分來執(zhí)行。
因此,用于非公有方法的句柄或者用于非公有類中的方法的句柄一般應(yīng)當(dāng)保密。它們不應(yīng)當(dāng)被傳遞給不受信任的代碼,除非從不受信任的代碼使用它們是無害的。
方法句柄創(chuàng)建
Java代碼可以創(chuàng)建直接訪問該代碼可訪問的任何方法、構(gòu)造函數(shù)或字段的方法句柄。這是經(jīng)由反射的、基于能力的API(被稱為MethodHandles.Lookup)來完成的。例如,靜態(tài)方法句柄可以從Lookup.findStatic獲得。還存在來自核心反射API對象的轉(zhuǎn)換方法,諸如Lookup.unreflect。
類似于類和字符串,對應(yīng)于可訪問字段、方法和構(gòu)造函數(shù)的方法句柄還可以在類文件的常量池中被直接表示為要由ldc字節(jié)碼加載的常量。新類型的常量池條目CONSTANT_MethodHandle直接指向關(guān)聯(lián)的CONSTANT_Methodref、CONSTANT_InterfaceMethodref或CONSTANT_Fieldref常量池條目。(關(guān)于方法句柄常量的詳情,請參見Java虛擬機(jī)規(guī)范的節(jié)4.4.8和節(jié)5.4.3.5)。
通過來自具有可變元數(shù)修飾符位(0x0080)的方法或構(gòu)造函數(shù)的查找或常量加載而產(chǎn)生的方法句柄具有對應(yīng)的可變元數(shù),就像它們是在asVarargsCollector的幫助下定義的一樣。
方法引用可以參考靜態(tài)或非靜態(tài)方法。在非靜態(tài)的情況下,方法句柄類型包括置于任何其它變元之前的顯式接收者變元。在方法句柄的類型中,根據(jù)最初在其下請求該方法的類來類型化初始接收者變元。(例如,如果非靜態(tài)方法句柄是經(jīng)由ldc獲得的,則接收者的類型是在常量池條目中命名的類)。
方法句柄常量經(jīng)受與它們對應(yīng)的字節(jié)碼指令相同的鏈接時(shí)間訪問檢查,并且ldc指令將拋出對應(yīng)的鏈接錯(cuò)誤,如果字節(jié)碼行為將拋出這樣的錯(cuò)誤的話。
作為其結(jié)果,對受保護(hù)成員的訪問僅被限制于訪問類或它的子類中的一個(gè)子類的接收者,并且訪問類又必須是該受保護(hù)成員的定義類的子類(或包兄弟)。如果方法引用參考受保護(hù)的非靜態(tài)方法或者當(dāng)前包之外的類的字段,則接收者變元將被縮小為訪問類的類型。
當(dāng)啟用虛方法的方法句柄時(shí),總是在接收者中查找該方法(即,第一個(gè)變元)。
還可以創(chuàng)建具體虛方法實(shí)現(xiàn)的非虛(non-virtual)方法句柄。這些不基于接收者類型執(zhí)行虛擬查找。這樣的方法句柄模擬同一方法的invokespecial指令的效果。
使用示例
這是使用的一些示例:
Object x,y;String s;int i;
MethodType mt;MethodHandle mh;
MethodHandles.Lookup lookup=MethodHandles.lookup();
//mt is(char,char)String
mt=MethodType.methodType(String.class,char.class,char.class);
mh=lookup.findVirtual(String.class,"replace",mt);
s=(String)mh.invokeExact("daddy",'d','n');
//invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
assertEquals(s,"nanny");
//weakly typed invocation(using MHs.invoke)
s=(String)mh.invokeWithArguments("sappy",'p','v');
assertEquals(s,"savvy");
//mt is(Object[])List
mt=MethodType.methodType(java.util.List.class,Object[].class);
mh=lookup.findStatic(java.util.Arrays.class,"asList",mt);
assert(mh.isVarargsCollector());
x=mh.invoke("one","two");
//invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assertEquals(x,java.util.Arrays.asList("one","two"));
//mt is(Object,Object,Object)Object
mt=MethodType.genericMethodType(3);
mh=mh.asType(mt);
x=mh.invokeExact((Object)1,(Object)2,(Object)3);
//invokeExact(Ljava/lang/Object;Ljava/lang/Object;
Ljava/lang/Object;)Ljava/lang/Object;
assertEquals(x,java.util.Arrays.asList(1,2,3));
//mt is()int
mt=MethodType.methodType(int.class);
mh=lookup.findVirtual(java.util.List.class,"size",mt);
i=(int)mh.invokeExact(java.util.Arrays.asList(1,2,3));
//invokeExact(Ljava/util/List;)I
assert(i==3);
mt=MethodType.methodType(void.class,String.class);
mh=lookup.findVirtual(java.io.PrintStream.class,"println",mt);
mh.invokeExact(System.out,"Hello,world.");
//invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
對invokeExact或普通的invoke的上面的調(diào)用中的每個(gè)調(diào)用生成具有在以下注釋中指示的符號類型描述符的單個(gè)invokevirtual指令。在這些示例中,輔助器方法assertEquals被假設(shè)是對它的變元調(diào)用Objects.equals并且聲稱結(jié)果為真的方法。
異常
方法invokeExact和invoke被聲明為拋出Throwable(可拋出),這就是說,不存在對于方法句柄可以拋出什么的靜態(tài)限制。由于JVM不區(qū)分已檢查和未檢查的異常(當(dāng)然,除了通過它們的類區(qū)分之外),因此將已檢查的異常歸因于方法句柄啟用對字節(jié)碼形狀沒有特別的影響。但是在Java源代碼中,執(zhí)行方法句柄調(diào)用的方法必須顯式拋出Throwable,否則必須本地捕獲所有的Throwable,從而僅重新拋出在上下文中合法的Throwable,并且包裝非法的Throwable。
簽名多態(tài)性
invokeExact和普通的invoke的不同尋常的編譯和鏈接行為由術(shù)語簽名多態(tài)性引用。如在Java語言規(guī)范中所定義的,簽名多態(tài)方法是可以與大范圍的調(diào)用簽名和返回類型中的任何調(diào)用簽名和返回類型一起操作的方法。
在源代碼中,不管所請求的符號類型描述符如何,對簽名多態(tài)方法的調(diào)用將編譯。通常,Java編譯器針對命名的方法發(fā)出具有給定符號類型描述符的invokevirtual指令。不同尋常的部分是符號類型描述符從實(shí)際的變元和返回類型派生,而不是從方法聲明派生。
當(dāng)JVM處理包含簽名多態(tài)調(diào)用的字節(jié)碼時(shí),它將成功鏈接任何這樣的調(diào)用,而不管其符號類型描述符如何。(為了保持類型安全,JVM將利用合適的動(dòng)態(tài)類型檢查來保衛(wèi)這樣的調(diào)用,如其他地方所描述的)。
要求包括編譯器后端的字節(jié)碼生成器發(fā)出用于這些方法的未變換的符號類型描述符。要求確定符號鏈接的工具接受這樣的未變換的描述符,而無需報(bào)告鏈接錯(cuò)誤。
方法句柄和核心反射API之間的互操作
通過在Lookup API中使用工廠方法,由核心反射API對象表示的任何類成員可以被轉(zhuǎn)換為行為上等價(jià)的方法句柄。例如,可以使用Lookup.unreflect將反射方法轉(zhuǎn)換為方法句柄。得到的方法句柄一般提供對底層類成員的更直接和高效的訪問。
作為特殊情況,當(dāng)核心反射API被用來查看該類中的簽名多態(tài)方法invokeExact或普通的invoke時(shí),它們看起來就像平常的非多態(tài)方法。如由Class.getDeclaredMethod看到的,它們的反射外觀不受該API中的它們的具體狀態(tài)影響。例如,Method.getModifiers將確切地報(bào)告對于任何類似聲明的方法所需的那些修飾符位,在這種情況下包括native和varargs位。
與任何反射方法一樣,這些方法(當(dāng)被反射時(shí))可以經(jīng)由java.lang.reflect.Method.invoke被啟用。然而,這樣的反射調(diào)用不會導(dǎo)致方法句柄啟用。這樣的調(diào)用如果被傳遞了所需的變元(類型Object[]的單個(gè)變元),則將忽略該變元并且將拋出UnsupportedOperationException。
由于invokevirtual指令可以在任何符號類型描述符下本機(jī)啟用方法句柄,因此這種反射視圖與這些方法經(jīng)由字節(jié)碼的正常呈現(xiàn)沖突。因此,這兩個(gè)本機(jī)方法在通過Class.getDeclaredMethod被反射查看時(shí)可以僅被視為占位符。
為了獲得用于特定類型描述符的啟用者方法,使用MethodHandles.exactInvoker或MethodHandles.invoker。Lookup.findVirtual API還能夠?yàn)槿魏沃付ǖ念愋兔枋龇祷赜糜谡{(diào)用invokeExact或普通的invoke的方法句柄。
方法句柄和Java泛型之間的互操作
可以對利用Java泛型類型聲明的方法、構(gòu)造函數(shù)或字段獲得方法句柄。與核心反射API一樣,方法句柄的類型將從源級類型的擦除構(gòu)建。當(dāng)方法句柄被啟用時(shí),它的變量的類型或返回值轉(zhuǎn)換類型可以是泛型類型或類型實(shí)例。如果這種情況發(fā)生,則編譯器將在它構(gòu)造用于invokevirtual指令的符號類型描述符時(shí)用它們的擦除來替換那些類型。
因?yàn)轭愃坪瘮?shù)(function-like)的類型和參數(shù)化的Java類型之間存在三方面不匹配,所以方法句柄不用Java參數(shù)化的(泛型)類型來表示它們的類似函數(shù)的類型。
方法類型涉及(range over)從無變元到多達(dá)允許的變元的最大數(shù)目的所有可能的元數(shù)。泛型不是可變變元的(variadic),因此不能表示這一點(diǎn)。
方法類型可以指定Java泛型類型不能涉及的原語類型的變元。
方法句柄之上的更高階函數(shù)(組合器)常常是跨大范圍的函數(shù)類型(包括多個(gè)元數(shù)的函數(shù)類型)通用的。用Java類型參數(shù)來表示這種通用性是不可能的。
Lookup工廠方法
對Lookup對象的工廠方法與用于方法、構(gòu)造函數(shù)和字段的所有主要用例對應(yīng)。由工廠方法創(chuàng)建的每個(gè)方法句柄是特定字節(jié)碼行為的功能等價(jià)物。(字節(jié)碼行為在Java虛擬機(jī)規(guī)范的節(jié)5.4.3.5中描述。)這是這些工廠方法與得到的方法句柄的行為之間的對應(yīng)關(guān)系的總結(jié):
這里,類型C是正在被搜索成員的類或接口,其在查找方法中被記錄為名為refc的參數(shù)。方法類型MT由返回類型T以及變元類型A*的序列組成。構(gòu)造函數(shù)也具有變元類型A*的序列并且被認(rèn)為返回類型C的新創(chuàng)建的對象。MT和字段類型FT二者都被記錄為名為type的參數(shù)。形式參數(shù)this表示類型C的自引用;如果它存在,則它總是方法句柄啟用的首要變元。(在一些protected(受保護(hù))成員的情況下,this可以在類型方面被限制為查找類;參見下文。)名稱arg表示所有其它方法句柄變元。在用于核心反射API的代碼示例中,如果所訪問的方法或字段是靜態(tài)的,則名稱thisOrNull表示空引用,否則名稱thisOrNull表示this。名稱aMethod、aField和aConstructor表示對應(yīng)于給定成員的反射對象。
在給定成員具有可變元數(shù)的情況下(即,給定成員是方法或構(gòu)造函數(shù)),返回的方法句柄也將具有可變元數(shù)。在所有其它情況下,返回的方法句柄將具有固定元數(shù)。
討論:查找方法句柄與底層類成員和字節(jié)碼行為之間的等價(jià)性可以以若干方式被破壞。如果C不是從查找類的加載器可符號訪問的,則查找仍然可以成功,即使沒有等價(jià)的Java表達(dá)式或字節(jié)碼常量。同樣,如果T或MT不是從查找類的加載器可符號可訪問的,則查找仍然可以成功。例如,不管所請求的類型如何,對MethodHandle.invokeExact和MethodHandle.invoke的查找將總是成功。如果安裝了安全管理器,則它可以出于各種原因禁止查找(參見下文)。相比之下,對CONSTANT_MethodHandle常量的ldc指令不經(jīng)受安全管理器檢查。如果查找方法具有非常大的元數(shù),則由于方法句柄類型具有太多參數(shù),因此方法句柄創(chuàng)建可能失敗。
訪問檢查
當(dāng)創(chuàng)建方法句柄時(shí),在Lookup的工廠方法中應(yīng)用訪問檢查。這是與核心反射API的主要區(qū)別,因?yàn)閖ava.lang.reflect.Method.invoke在每次調(diào)用時(shí)對每個(gè)調(diào)用者執(zhí)行訪問檢查。
所有訪問檢查從Lookup對象開始,Lookup對象將其記錄的查找類與所有請求進(jìn)行比較以創(chuàng)建方法句柄。單個(gè)Lookup對象可被用來創(chuàng)建任何數(shù)量的訪問檢查過的方法句柄,所有方法句柄都針對單個(gè)查找類進(jìn)行檢查。
Lookup對象可以與其它受信任代碼(諸如元對象協(xié)議)共享。共享的Lookup對象委托對查找類的私有成員創(chuàng)建方法句柄的能力。即使有特權(quán)的代碼使用Lookup對象,訪問檢查也局限為原始查找類的特權(quán)。
查找可能失敗,因?yàn)榘悓τ诓檎翌悂碚f不可訪問,或者因?yàn)槠谕念惓蓡T丟失,或者因?yàn)槠谕念惓蓡T對于查找類來說不可訪問,或者因?yàn)椴檎覍ο蟛槐蛔銐蛐湃我栽L問成員。在這些情況中的任何情況下,將從嘗試的查找中拋出ReflectiveOperationException。確切的類將是以下之一:NoSuchMethodException–如果方法被請求但是不存在;NoSuchFieldException–如果字段被請求但是不存在;IllegalAccessException–如果成員存在但是訪問檢查失敗。
一般而言,可以為方法M查找方法句柄的條件不比查找類可以編譯、驗(yàn)證和解析對M的調(diào)用的條件更嚴(yán)格。在JVM將引發(fā)像NoSuchMethodError一樣的異常的情況下,方法句柄查找一般將引發(fā)對應(yīng)的檢查異常,諸如NoSuchMethodException。而且啟用由查找產(chǎn)生的方法句柄的效果完全等價(jià)于執(zhí)行對M的編譯后、驗(yàn)證后和解析后的調(diào)用。對于字段和構(gòu)造函數(shù)也是如此。
討論:訪問檢查僅適用于命名和反射方法、構(gòu)造函數(shù)和字段。諸如MethodHandle.asType之類的其它方法句柄創(chuàng)建方法不要求任何訪問檢查,并且獨(dú)立于任何Lookup對象被使用。
如果期望的成員是protected(受保護(hù)的),則包括查找類必須在與期望成員相同的包中或者必須繼承該成員的要求的通常的JVM規(guī)則適用。(請參見Java虛擬機(jī)規(guī)范,節(jié)4.9.2、5.4.3.5和6.4。)另外,如果期望的成員是不同包中的非靜態(tài)字段或方法,則得到的方法句柄僅適用于查找類或它的子類中的一個(gè)子類的對象。這個(gè)需求通過將首要的this參數(shù)的類型從C(這將必然是查找類的超類)縮小到查找類本身來實(shí)施。
JVM對invokespecial指令施加類似的要求,即,接收者變元必須匹配解析的方法和當(dāng)前類二者。同樣,這個(gè)要求通過將首要參數(shù)的類型縮小為得到的方法句柄來實(shí)施。(請參見Java虛擬機(jī)規(guī)范,節(jié)4.10.1.9。)
JVM將構(gòu)造函數(shù)和靜態(tài)初始化器塊表示為具有特殊名稱的內(nèi)部方法(“<init>”和“<clinit>”)。啟用指令的內(nèi)部語法允許它們參考這樣的內(nèi)部方法,就好像它們是正常的方法一樣,但是JVM字節(jié)碼驗(yàn)證器拒絕它們。這樣的內(nèi)部方法的查找將產(chǎn)生NoSuchMethodException。
在一些情況下,嵌套類之間的訪問由Java編譯器通過創(chuàng)建包裝器方法以訪問同一最高級聲明中另一個(gè)類的私有方法來獲得。例如,嵌套類C.D可以訪問其它相關(guān)類(諸如,C、C.D.E或C.B)內(nèi)的私有成員,但是Java編譯器可能需要在那些相關(guān)類中生成包裝器方法。在這樣的情況下,C.E上的Lookup對象將不能訪問那些私有成員。對這種限制的解決方法是Lookup.in方法,它可以將對C.E的查找變換為對那些其它類中的任何類的查找,而無需特殊的特權(quán)提升。
對給定查找對象的允許的訪問可以根據(jù)其lookupMode集合被限制為查找類正常可訪問的成員子集。例如,publicLookup方法產(chǎn)生僅允許訪問公有類中的公有成員的查找對象。調(diào)用者敏感的方法lookup產(chǎn)生具有相對于其調(diào)用者類的全部能力的查找對象,以模擬所有支持的字節(jié)碼行為。另外,Lookup.in方法可以產(chǎn)生具有比原始查找對象更少的訪問模式的查找對象。
私有訪問的討論:如果查找的查找模式包括訪問private(私有)成員的可能性,則我們稱查找具有私有訪問。如在其它地方的相關(guān)方法中所記錄的,只有具有私有訪問的查找才擁有以下能力:
訪問查找類的私有字段、方法和構(gòu)造函數(shù)
創(chuàng)建啟用調(diào)用者敏感的方法(諸如Class.forName)的方法句柄
創(chuàng)建模擬invokespecial指令的方法句柄
避免對于查找類可訪問的類的包訪問檢查
創(chuàng)建具有對同一包成員內(nèi)的其它類的私有訪問的委托的查找對象
這些許可中的每個(gè)許可是具有私有訪問的查找對象可以被安全地追溯到起源類的事實(shí)的結(jié)果,該起源類的字節(jié)碼行為和Java語言訪問許可可以由方法句柄可靠地確定和模擬。
安全管理器交互
雖然字節(jié)碼指令僅可以參考相關(guān)類加載器中的類,但是這個(gè)API可以搜索任何類中的方法,只要對它的Class對象的引用是可用的。這樣的交叉加載器引用對于核心反射API也是可能的,并且對諸如invokestatic或getfield之類的字節(jié)碼指令是不可能的。存在安全管理器API以允許應(yīng)用檢查這樣的交叉加載器引用。這些檢查適用于(如在Class上找到的)MethodHandles.Lookup API和Core Reflection API二者。
如果存在安全管理器,則成員查找經(jīng)受附加的檢查。對安全管理器進(jìn)行一至三次調(diào)用。這些調(diào)用中的任何調(diào)用可以通過拋出SecurityException來拒絕訪問。將smgr定義為安全管理器,將lookc定義為當(dāng)前查找對象的查找類,將refc定義為在其中查找成員的包含類,以及將defc定義為成員在其中被實(shí)際定義的類。如果當(dāng)前查找對象不具有私有訪問,則值lookc被定義為不存在。調(diào)用是根據(jù)以下規(guī)則進(jìn)行的:
步驟1:如果lookc不存在,或者如果它的類加載器與refc的類加載器不相同或者不是refc的類加載器的祖先,則調(diào)用smgr.checkPackageAccess(refcPkg),其中refcPkg是refc的包。
步驟2:如果檢索出的成員不是公有的并且lookc不存在,則調(diào)用具有RuntimePermission(“accessDeclaredMembers”)的smgr.checkPermission。
步驟3:如果檢索出的成員不是公有的,并且如果lookc不存在,并且如果defc和refc不同,則調(diào)用smgr.checkPackageAccess(defcPkg),其中defcPkg是defc的包。
安全檢查在其它訪問檢查已通過之后被執(zhí)行。因此,上面的規(guī)則預(yù)先假定成員是公有的,否則是正從具有訪問該成員的權(quán)限的查找類訪問的成員。
調(diào)用者敏感的方法
少數(shù)Java方法具有被稱為調(diào)用者敏感性的特殊性質(zhì)。調(diào)用者敏感的方法可以取決于其直接調(diào)用者的身份而表現(xiàn)不同。
如果請求對于調(diào)用者敏感的方法的方法句柄,則用于字節(jié)碼行為的通用規(guī)則適用,但是它們以特殊方式考慮查找類。得到的方法句柄表現(xiàn)得好像它是從包含在查找類中的指令被調(diào)用的一樣,以便調(diào)用者敏感的方法檢測該查找類。(相比之下,方法句柄的啟用者被忽略。)因此,在調(diào)用者敏感的方法的情況下,不同的查找類可能產(chǎn)生表現(xiàn)不同的方法句柄。
在查找對象是publicLookup()或者不具有私有訪問的某些其它查找對象的情況下,查找類被忽略。在這樣的情況下,不可以創(chuàng)建調(diào)用者敏感的方法句柄,訪問被禁止,并且查找失敗并且拋出IllegalAccessException。
討論:例如,取決于調(diào)用調(diào)用者敏感的方法Class.forName(x)的類的類加載器,調(diào)用者敏感的方法Class.forName(x)可以返回不同的類或拋出不同的異常。Class.forName的公有查找將失敗,因?yàn)闆]有合理的方式來確定它的字節(jié)碼行為。
如果應(yīng)用高速緩存方法句柄以用于廣泛共享,則它應(yīng)當(dāng)使用publicLookup()來創(chuàng)建它們。如果存在Class.forName的查找,則它將失敗,并且在該情況下應(yīng)用必須采取適當(dāng)?shù)膭?dòng)作。也許在引導(dǎo)方法的啟用期間的稍后的查找可以結(jié)合調(diào)用者的特殊身份,從而使得該方法可訪問。
函數(shù)MethodHandles.lookup是調(diào)用者敏感的,以便于可以存在用于查找的安全基礎(chǔ)。JSR 292 API中的幾乎所有其它方法依賴于查找對象來檢查訪問請求。
方法句柄和invokedynamic
抽象地來說,方法句柄僅是類型和符合該類型的某些行為。由于適合面向?qū)ο蟮南到y(tǒng),因此行為可以包括數(shù)據(jù)。具體來說,方法句柄可以參考任何JVM方法、字段或構(gòu)造函數(shù),否則它可以是任何先前指定的方法句柄的變換。變換包括部分應(yīng)用(綁定)、過濾和各種形式的變元混排(shuffling)。
方法句柄的類型被表示為零個(gè)或更多個(gè)參數(shù)類型的序列,以及可選的返回類型(或者無類型void)。具體來說,這是MethodType引用,并且可以使用MethodHandle.type從任何方法句柄提取。
該行為是當(dāng)方法句柄被啟用時(shí)使用方法MethodHandle.invokeExact發(fā)生的行為。方法句柄的特殊能力在于invokeExact接受任何數(shù)量的任何類型的變元,并且可以返回任何類型或void。常規(guī)的invokevirtual指令執(zhí)行該操作。(它被秘密地重寫為invokehandle(如下文所討論的),但是除了HotSpot實(shí)現(xiàn)器之外,這可以被忽略。)
對于方法句柄特有地,invokevirtual指令可以指定任何結(jié)構(gòu)上有效的類型簽名,并且調(diào)用點(diǎn)將鏈接。在技術(shù)上,我們稱invokeExact是簽名多態(tài)的。實(shí)際而言,當(dāng)鏈接這樣的調(diào)用點(diǎn)時(shí),JVM可以準(zhǔn)備好處理任何類型的簽名,這意味著它將必須生成各種各樣的適配器。從用戶的角度來看,方法句柄可以包裝和/或啟用任何類型的任何方法。
具體來說,方法句柄的行為取決于被稱為LambdaForm的對象,該對象是逐步操作的低級描述。方法句柄的lambda形式被存儲在它的形式字段中,就好像它的類型被存儲在它的類型字段中一樣。
方法句柄的lambda形式可以完全忽略方法句柄,并且進(jìn)行與上下文無關(guān)的某些操作,比如拋出異?;蚍祷亓?。更一般地,它可以查詢方法句柄以獲得信息。例如,它可以檢查方法句柄的返回類型,并且在返回某個(gè)值之前將它轉(zhuǎn)換為該類型。
更有趣的是,如果方法句柄的類是包含附加數(shù)據(jù)字段的子類,則lambda形式可以在它執(zhí)行時(shí)參考那些字段。
由于方法句柄表達(dá)行為多于表達(dá)狀態(tài),因此它們的字段通常是不可變的。然而,方法句柄可以容易地被綁定到任意Java對象,從而產(chǎn)生閉包(closure)。
“基本類型”系統(tǒng)
為了更簡單地實(shí)現(xiàn)簽名多態(tài)性,方法句柄在內(nèi)部依據(jù)基本類型操作?;绢愋褪瞧渲性S多不方便的區(qū)別已被“擦除”的JVM類型,以便于剩余的區(qū)別(諸如引用vs.原語和整型vs.長整型)可以被注意。
對于初學(xué)者,在基本類型系統(tǒng)中,除了浮點(diǎn)型之外的所有32位類型都被擦除成簡單的整型。如果在某個(gè)地方需要字節(jié)值,則它可以從完整的整型被遮蔽。因此,僅有四種原語類型需要擔(dān)心。
依據(jù)基本類型化規(guī)則,所有引用類型由java.lang.Object表示。因此,總共有五種基本類型,這五種基本類型由它們的JVM簽名字符表示為:L、I、J、F、D。我們將用于無類型void的V添加到這些基本類型.
在Java代碼的大部分中,完整類型系統(tǒng)是生效的。為了命名引用類型,可以查詢和兌現(xiàn)(honor)類加載器和類型約束的系統(tǒng)。從JSR 292運(yùn)行環(huán)境的角度來看,該類型系統(tǒng)是名稱和范圍的復(fù)雜組合。在運(yùn)行環(huán)境內(nèi)部,除了Object和引導(dǎo)(boot)類路徑上的其它類型,使用基本類型沒有名稱需要擔(dān)心。
如果在某個(gè)地方需要較窄類型的引用,則可以在使用該引用之前發(fā)出顯式的檢查轉(zhuǎn)換(checkcast)。事實(shí)上,檢查轉(zhuǎn)換一般是對Class.cast的調(diào)用,其中專用類型是常量Class引用而不是符號引用名稱。
通常,所有額外的轉(zhuǎn)換(諸如整型到字節(jié)型以及Object到命名的引用類型)在優(yōu)化器中不出現(xiàn),優(yōu)化器對來自上下文的完整類型信息保持跟蹤。
lambda形式基礎(chǔ)
簡而言之,lambda形式是具有零個(gè)或更多個(gè)形式參數(shù)、加上零個(gè)或更多個(gè)主體表達(dá)式的典型lambda表達(dá)式。參數(shù)和表達(dá)式值的類型是從基本類型系統(tǒng)汲取的。每個(gè)表達(dá)式僅是方法句柄到零個(gè)或更多個(gè)變元的應(yīng)用。每個(gè)變元或者是常量值或者是先前指定的參數(shù)或表達(dá)式值。
當(dāng)lambda形式被用作方法句柄行為時(shí),第一個(gè)參數(shù)(a0)總是方法句柄本身。(但是對于lambda形式來說還存在其它用法。)當(dāng)方法句柄被啟用時(shí),在任何初始的類型檢查之后,JVM執(zhí)行方法句柄的lambda形式以完成方法句柄啟用。這導(dǎo)致一些引導(dǎo)挑戰(zhàn),因?yàn)閘ambda形式通過評估附加的方法句柄啟用來執(zhí)行。
lambda形式優(yōu)化
在lambda形式執(zhí)行中還存在允許系統(tǒng)優(yōu)化自身的一個(gè)間接尋址(indirection):lambda形式具有被稱為vmentry的、(最后)為JVM提供要跳轉(zhuǎn)到的Method*指針的字段,以便評估lambda形式。(注意:由于Java不能直接表示JVM元數(shù)據(jù)指針,因此該vmentry實(shí)際上具有類型MemberName,MemberName是用于Method*的低級包裝器。因此畢竟還存在一個(gè)間接尋址來隱藏元數(shù)據(jù)。)
當(dāng)首次創(chuàng)建lambda形式時(shí),該vmentry指針被初始化為被稱為lambda形式解釋器的方法,該方法可以執(zhí)行任何lambda形式。(實(shí)際上它具有專用于變元的元數(shù)和基本類型的瘦包裝器。)lambda形式解釋器非常簡單并且慢。在它執(zhí)行幾十次給定的lambda形式之后,解釋器取來(fetch)或生成用于lambda形式的字節(jié)碼,該字節(jié)碼(至少部分地)被定制為lambda形式主體。在穩(wěn)定狀態(tài)下,所有“熱”方法句柄及其“熱”lambda形式使字節(jié)碼被生成,并且使字節(jié)碼最終被JIT編譯。
因此,在穩(wěn)定狀態(tài)下,在沒有l(wèi)ambda形式解釋器的情況下,熱方法句柄被執(zhí)行。低級JVM步驟如下:1.取來MethodHandle.form;2.取來LambdaForm.vmentry;3.取來隱藏的Method*指針MemberName.vmtarget;4.取來Method::from_compiled_entry;5.跳轉(zhuǎn)到優(yōu)化的代碼。如在別處所指出的,如果方法句柄(或者如果lambda形式或成員名)是編譯時(shí)常量,則可以完成所有通常的內(nèi)聯(lián)。
Invokedynamic
如在JVM中所定義的,invokedynamic包括名稱、方法類型簽名和引導(dǎo)指定符。調(diào)用者可見的指令行為僅由類型簽名來定義,該類型簽名確切地確定哪些類型的變元和返回值通過棧被混排。當(dāng)指令首次被執(zhí)行時(shí),確定指令的實(shí)際行為。與其它啟用指令一樣,LinkResolver模塊處理在第一次執(zhí)行時(shí)被執(zhí)行的設(shè)置操作。
對于invokedynamic,引導(dǎo)指定符被解析為方法句柄以及零個(gè)或更多個(gè)額外的常量變元。(這些都是從常量池汲取的。)名稱和簽名連同額外的變元和MethodHandles.Lookup參數(shù)一起被推送到棧上,以具體化請求類,并且引導(dǎo)方法句柄被啟用。(這種對用戶指定的方法的訴求可能看起來令人吃驚,但是對JVM來說,它不比為了定位新類的字節(jié)碼而可以執(zhí)行的ClassLoader(類加載器)操作復(fù)雜多少。)
當(dāng)引導(dǎo)方法返回時(shí),它向JVM運(yùn)行環(huán)境給出CallSite對象。這個(gè)調(diào)用點(diǎn)包含方法句柄,它(最終)確定鏈接的invokedynamic指令的確切行為。由于方法句柄差不多可以做任何事情,因此invokedynamic指令在鏈接之后是完全一般的虛擬機(jī)指令。
(細(xì)心的讀者將想知道為什么引導(dǎo)方法不僅僅返回方法句柄。答案是一些調(diào)用點(diǎn)潛在地可以隨時(shí)間被綁定到一系列不同的方法句柄。這給予Java程序員類似于由JVM用來管理單態(tài)虛擬調(diào)用點(diǎn)和多態(tài)虛擬調(diào)用點(diǎn)的低級代碼打補(bǔ)丁技術(shù)。)
invokedynamic實(shí)現(xiàn)
因?yàn)槊總€(gè)invokedynamic指令(一般)鏈接到不同的調(diào)用點(diǎn),所以常量池高速緩存可以包含用于每個(gè)invokedynamic指令的分開的條目。(如果其他啟用指令使用常量池中的同一符號引用,則它們可以共享CP高速緩存條目。)CP高速緩存條目(“CPCE”)在被解析時(shí)具有元數(shù)據(jù)和/或偏移信息的一個(gè)詞或兩個(gè)詞。
對于invokedynamic,解析后的CPCE包含到提供調(diào)用的確切行為的具體adapter(適配器)方法的Method*指針。還存在與被稱為appendix(附錄)的調(diào)用點(diǎn)相關(guān)聯(lián)的引用參數(shù),該引用參數(shù)被存儲在用于CPCE的resolved_references數(shù)組中。
該方法被稱為適配器,因?yàn)?一般來說)它混排變元、從調(diào)用點(diǎn)提取目標(biāo)方法句柄,并且啟用該方法句柄。額外的引用參數(shù)被稱為附錄,因?yàn)樗趇nvokedynamic指令被執(zhí)行時(shí)附加到變元列表。通常,附錄是由引導(dǎo)方法產(chǎn)生的CallSite引用,但是JVM對此不關(guān)心。只要CPCE中的適配器方法知道如何處理與CPCE一起存儲的附錄,就一切都好。
作為極端情況,如果附錄值為空,則它根本不被推送,并且適配器方法可能不期望額外的變元。在這種情況下,適配器方法可以是對具有與invokedynamic指令一致的簽名的靜態(tài)方法的永久鏈接的引用。這將實(shí)際上將invokedynamic變成簡單的invokestatic。許多其它這樣的強(qiáng)度降低(strength reduction)優(yōu)化是可能的。
鏈接握手
用于invokedynamic CPCE的適配器Method*指針不是由JVM選擇,而是由受信任的Java代碼選擇。對于附錄引用也是如此。事實(shí)上,JVM不直接啟用引導(dǎo)方法。相反,JVM利用解析后的引導(dǎo)指定符信息來調(diào)用特定于HotSpot的方法MethodHandleNatives.linkCallSite。(其它JVM實(shí)現(xiàn)不一定使用這種握手。)linkCallSite方法執(zhí)行JSR 292引導(dǎo)規(guī)則所要求的步驟,并且返回兩個(gè)協(xié)調(diào)值、適配器方法及其附錄。
由于Java不能表示原始Method*指針,因此該方法被包裝在被稱為MemberName的私有Java類型中,這類似于用于Klass*的Java鏡像。附錄是簡單的Object引用(或null)。在經(jīng)過些許解包之后,這些被插入到CPCE中。
用于invokedynamic的適配器方法
一般而言,適配器方法是由JSR 292運(yùn)行環(huán)境運(yùn)行中(on the fly)創(chuàng)建的特殊生成的方法。它從計(jì)算當(dāng)前調(diào)用點(diǎn)目標(biāo)并且啟用該目標(biāo)的lambda形式生成。lambda形式采用與為invokedynamic指令入棧的變元對應(yīng)的首要參數(shù),即,由該指令的方法簽名所需的參數(shù)。lambda形式還采用尾隨的附錄變元(如果相關(guān)的話)。然后它執(zhí)行引導(dǎo)方法及其調(diào)用點(diǎn)所需的任何操作。
這是取自實(shí)際應(yīng)用的適配器方法的示例:
這里,invokedynamic指令采用兩個(gè)變元,double a0和引用a1,并且返回引用t4。附錄跟隨在結(jié)尾處,在a2中。
Lambda形式的主體使用子例程Invokers.getCallSiteTarget從附錄提取方法句柄目標(biāo)。方法句柄被綁定到t3,然后立即對兩個(gè)首要參數(shù)a0和a1啟用。
如通過檢查Java代碼可以看到的,getCallSiteTarget期望獲得非空的CallSite變元。如果這將失敗,則它將意味著受信任的Java代碼中有錯(cuò)誤(bug),因?yàn)槭苄湃未a負(fù)責(zé)向JVM返回一對一致的適配器和附錄。
特殊的非公有例程MethodHandle.invokeBasic是MethodHandle.invokeExact的未檢查版本。它在兩個(gè)方面與invokeExact不同。第一,它不檢查其被調(diào)用者具有與調(diào)用點(diǎn)處的類型(完全)匹配的類型。(不管怎樣,它可以不拋出WrongMethodTypeException。)
第二,它允許根據(jù)在JSR 292運(yùn)行環(huán)境中使用的基本類型方案來放寬其變元和返回值的類型化。(參見上文。)
invokedynamic的示例執(zhí)行序列
invokedynamic指令的調(diào)用點(diǎn)的目標(biāo)可以是任何方法句柄。在最簡單的情況下,它可以是將包含invokedynamic指令的方法連接到某些其它Java語言方法的直接方法句柄。
這是將進(jìn)行從方法IndyUser.m1到目標(biāo)方法LibraryCls.m2的這樣的連接的事件和棧幀的序列的示例:
存在兩個(gè)內(nèi)部棧幀,一個(gè)用于綁定到invokedynamic調(diào)用點(diǎn)的適配器,一個(gè)處理用于目標(biāo)方法句柄的啟用。
在[關(guān)于直接方法句柄的頁面]上解釋具體的方法internalMemberName和linkToStatic。
方法句柄啟用
在(rewriter.cpp中的)HotSpot的內(nèi)部,方法句柄啟用被重寫,以使用被稱為invokehandle的特殊指令。這個(gè)指令在許多方面與invokedynamic相似。它解析為適配器方法指針和附錄。附錄(如果不為空)在invoke或invokeExact的顯式變元之后被推送。
解析經(jīng)由對受信任的Java代碼,被稱為MethodHandleNatives.linkMethod的方法的調(diào)用來完成。與linkCallSite一樣,JVM向linkMethod傳遞所有解析后的常量池引用,并接收回一對協(xié)調(diào)值MemberName和Object。在解包之后,這些作為適配器和附錄被插入到CPCE中。
相同的自由度適用于invokehandle CPCE條目,就像適用于invokedynamic CPCE條目一樣,并且類似的優(yōu)化機(jī)會適用。與invokedynamic有一個(gè)主要區(qū)別:如果許多invokehandle指令都具有同一的簽名和方法名稱(“invokeExact”vs.“invoke”),則它們可以共享單個(gè)CPCE條目。
用于invokeExact的適配器方法
MethodHandle.invokeExact的invokevirtual的標(biāo)準(zhǔn)語義是簡單的。用于調(diào)用點(diǎn)的簽名(其可以包含任何引用和原語類型的任何混合)被解析(在鏈接時(shí),被解析一次)為MethodType。每次方法句柄被啟用時(shí),將對照被啟用的方法句柄的類型來檢查方法類型。(由于方法句柄是計(jì)算出的值,所以它當(dāng)然可以每次不同,并且類型不一定匹配。)如果兩種類型在任何方面不同,則拋出WrongMethodTypeException。否則,將對給定的類型啟用方法句柄,并返回由調(diào)用者期望的類型的值。
用于invokeExact的適配器方法對應(yīng)地是簡單的。它僅僅執(zhí)行方法類型檢查,然后調(diào)用invokeBasic。附錄是對進(jìn)行類型檢查所需的resolvedMethodType的引用。
這是示例:
首要變元a0是方法句柄。結(jié)尾變元a2是在調(diào)用點(diǎn)被解析時(shí)計(jì)算的方法類型附錄。中間變元a1是方法句柄的唯一變元。
首先,對方法句柄和附錄調(diào)用子例程Invokers.checkExactType,從而提取來自方法句柄的類型并將其與靜態(tài)調(diào)用點(diǎn)類型進(jìn)行比較。如果沒有拋出異常,則控制返回到適配器方法。不返回任何值,并且名稱t3具有偽類型void。(由于附錄表示靜態(tài)調(diào)用點(diǎn)類型,并且方法句柄的類型是動(dòng)態(tài)可接受的類型,因此這可以被視為簡單的動(dòng)態(tài)類型檢查。)
接下來,invokeBasic被用來跳轉(zhuǎn)到方法句柄(現(xiàn)在已知它對于該調(diào)用是完全安全的)。簡稱為t4的結(jié)果返回,并且返回到調(diào)用者。
用于泛型啟用的適配器方法
方法句柄還支持較復(fù)雜的啟用模式,該較復(fù)雜的啟用模式可以對單獨(dú)的變元和返回值執(zhí)行類型轉(zhuǎn)換,并且甚至將變元分組成varargs數(shù)組。與invokeExact一樣,這樣的調(diào)用點(diǎn)的解析通過對MethodHandleNatives.linkMethod的調(diào)用來實(shí)現(xiàn)。在這種情況下,受信任的Java代碼可以返回更靈活和復(fù)雜的適配器方法。
這是示例:
如前所述,a0是方法句柄,結(jié)尾的a3是方法類型。這里有兩個(gè)常規(guī)變元:引用和整型。如前所述,第一個(gè)任務(wù)是檢查類型,并且這由Invokers.checkGenericType完成。與較簡單的檢查不同,該例程返回值。(如果必要,則該例程還可以拋出WrongMethodTypeException。)從checkGenericType返回的值實(shí)際上是方法句柄t4。該方法句柄立即對原始變元、加上原始方法句柄a0、加上期望的調(diào)用點(diǎn)類型a3(不按該順序)啟用。取決于a0的類型與調(diào)用點(diǎn)類型a3之間的匹配或不匹配,t4的實(shí)際行為可以非常簡單(基本上是另一個(gè)invokeBasic)或非常復(fù)雜(具有類型轉(zhuǎn)換的元數(shù)改變)。這取決于運(yùn)行環(huán)境。
用于invokeExact的示例執(zhí)行序列
invokeExact調(diào)用的接收者可以是任何種類的方法句柄。在最簡單的情況下,它可以是將包含方法句柄指令的啟用者的方法連接到某些其它Java語言方法的直接方法句柄。
這是將進(jìn)行從方法MHUser.m1到目標(biāo)方法LibraryCls.m2的這樣的連接的事件和棧幀的序列的示例:
與上面的invokedynamic示例的情況一樣,存在兩個(gè)內(nèi)部棧幀,一個(gè)用于綁定到invokeExact調(diào)用點(diǎn)的適配器,一個(gè)處理用于目標(biāo)方法句柄的啟用。