最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

invokedynamic指令的個(gè)人理解

2023-08-16 03:04 作者:lovexyn0827  | 我要投稿

前言

試著在3000字以內(nèi)講清自己對(duì)Java 7中引入的invokedynamic的理解。這篇文章主要省去了一些對(duì)Java API與JVM規(guī)范的引述,并側(cè)重于如何去理解以及該指令本身的使用的基本思路,而不是講解具體的使用規(guī)范。

文章如有缺漏錯(cuò)誤之處,歡迎反饋。

此處假定讀者掌握以下知識(shí):

  • Java語(yǔ)言

  • JVM的基本運(yùn)行模型

  • 基本的字節(jié)碼指令

  • ASM的基本結(jié)構(gòu)與簡(jiǎn)單使用

概述

invokedynamic這一指令由兩個(gè)詞語(yǔ)組成,即invoke(v., 調(diào)用)與dynamic(adv. & adj., 動(dòng)態(tài)的),顧名思義,這條指令可以訪問一個(gè)在運(yùn)行時(shí)動(dòng)態(tài)地確定而非硬編碼于類文件中的目標(biāo)。

簡(jiǎn)單來說,這條指令的執(zhí)行包括以下幾個(gè)步驟:

  1. 如果是第一次執(zhí)行,調(diào)用指令附加參數(shù)中給出的一個(gè)方法(BootstrapMethod)來確定該指令的行為,如調(diào)用哪一個(gè)方法或者是訪問哪一個(gè)字段。

  2. 按照第一次執(zhí)行時(shí)確定的行為執(zhí)行具體操作。

也就是說,invokedynamic指令的行為是在第一次被使用時(shí)才被確定的。

方法句柄MethodHandle

方法句柄代表了一個(gè)對(duì)類成員的基本操作,包括讀寫字段和調(diào)用方法等。可以認(rèn)為,一個(gè)方法句柄包含了一個(gè)具體成員的位置與其執(zhí)行的具體操作兩個(gè)屬性。

可以通過由java.invoke.MethodHandles.lookup()方法獲取的MethodHandles.Lookup實(shí)例中提供的工廠方法獲取MethodHandle實(shí)例。那些工廠方法大致可以分為兩類,一類是名稱類似findXXX()的方法,支持使用類似于反射的方式獲取方法句柄;另一類工廠方法的名稱類似于unflectXXX(),支持為已有的FieldMethod對(duì)象指定具體操作以將其轉(zhuǎn)換為方法句柄。

第一類工廠方法的形式大多類似于findXXX(class, name, type),三個(gè)參數(shù)分別為定義目標(biāo)成員的類相應(yīng)的Class實(shí)例、目標(biāo)成員的名稱與目標(biāo)成員的確切類型(或方法簽名)。唯一的例外是findConstructor方法,因?yàn)樗恍枰@式地指明名稱。在獲取操作方法的方法句柄時(shí),需要使用一個(gè)MethodType實(shí)例來表示方法簽名,這個(gè)實(shí)例可以通過工廠方法MethodType.methodType()獲取。

第二類工廠方法的形式比較簡(jiǎn)單,只接受一個(gè)FieldMethod實(shí)例,此處不再細(xì)說。

另外,這些工廠方法在執(zhí)行時(shí)通常會(huì)檢查曾獲取所用的Lookup實(shí)例的類是否能訪問目標(biāo)成員,如果失敗則拋出一個(gè)IllegalAccessException。在Java 9及以后的版本中,我們可以使用privateLookupIn()工廠方法獲取可以訪問調(diào)用類以外的其他類的私有成員的Lookup實(shí)例。對(duì)于第二類工廠方法,我們可以預(yù)先在用到的反射對(duì)象上調(diào)用setAccessible(true)來禁用這一訪問檢查,或許這也是Java 8中唯一可以獲取任意方法句柄的方案。

MethodHandles類中也提供了多個(gè)工廠方法以獲取或變換一些方法句柄,此處不再贅述。

調(diào)用站點(diǎn)CallSite

調(diào)用站點(diǎn)是一個(gè)方法句柄的容器,方法句柄只有被包含在調(diào)用站點(diǎn)中時(shí)才可以在invokedynamic指令中使用。

調(diào)用站點(diǎn)可以是可變的(MutableCallSite,VolatileCallSite),也可以是不可變的(ConstantCallSite)。不可變的調(diào)用站點(diǎn)可能會(huì)更高效,因?yàn)镴VM可以對(duì)其進(jìn)行一些優(yōu)化。

也可以創(chuàng)建自己的CallSite子類以實(shí)現(xiàn)一些自定義邏輯,如在調(diào)用次數(shù)超過一定值的前后提供不同的方法句柄。

BootstrapMethod

BootstrapMethod,簡(jiǎn)稱“BSM”,是一個(gè)用于在運(yùn)行時(shí)確定invokedynamic指令的具體行為的方法。當(dāng)然,Java 11中引入的動(dòng)態(tài)常量也使用了相同的技術(shù),但是這超出了本文的范圍,此處不再詳述。

一個(gè)BootstrapMethod通常是一個(gè)靜態(tài)方法,前三個(gè)參數(shù)的類型必須依次為:

  1. MethodHandles.Lookup

  2. String

  3. MethodType

這些參數(shù)后面還可以附加幾個(gè)參數(shù)用于傳遞一些附加信息。Java 10及之前的版本中,附加的參數(shù)類型可以為int、floatlong、double、StringMethodTypeMethodHandle。同時(shí),該方法必須返回一個(gè)CallSite實(shí)例。下方是一個(gè)簡(jiǎn)單的BootstrapMethod的定義:

Java 11中也允許借助動(dòng)態(tài)常量技術(shù)使用其他類型的附加參數(shù),以后會(huì)對(duì)其進(jìn)行專門講解。 在執(zhí)行該方法時(shí),傳入的參數(shù)依次是:

  • invokedynamic所在類通過MethodHandles.lookup()工廠方法獲取的MethodHandle實(shí)例;

  • invokedynamic指令指定的名稱;

  • 描述該invokedynamic行為的方法描述符

  • 附加的零至多個(gè)參數(shù) JVM標(biāo)準(zhǔn)中規(guī)定,也可以使用構(gòu)造器作為BootstrapMethod,只要那個(gè)構(gòu)造器能夠構(gòu)造出一個(gè)CallSite類型的對(duì)象。具體實(shí)現(xiàn)與使用靜態(tài)方法類似,此處不再贅述。 有必要說明,通過附加參數(shù)給出的MethodHandle不可以訪問invokedynamic指令所在類不可訪問的成員,否則在類的解析階段會(huì)因?yàn)樵L問檢查出錯(cuò)而拋出IllegalAccessError。

invokedynamic指令的格式

JVM字節(jié)碼中invokedynamic指令的格式非常簡(jiǎn)單:

其中,兩個(gè)index字節(jié)共同組成了一個(gè)指向常量池中一個(gè)CONSTANT_InvokeDynamic_info結(jié)構(gòu)的索引,該結(jié)構(gòu)直接或間接地提供了以下信息:

  • invokedynamic的名稱與對(duì)應(yīng)的方法描述符;

  • BootstrapMethod方法信息,可以指定一個(gè)靜態(tài)方法或構(gòu)造器;

  • BootstrapMethod方法的附加參數(shù)。 某種意義上也就是說,這三項(xiàng)信息是invokedynamic方法的固定參數(shù)。

在ASM中使用invokedynamic指令

Core API

可以由visitInvokeDynamicInsn()方法獲取或創(chuàng)建invokedynamic指令,其定義如下:


  • name:該invokedynmaic指令的名稱,只是傳入BootstrapMethod一個(gè)常量,可以按需要(隨便)設(shè)定;

  • descriptor:描述invokedynmaic指令行為方法一個(gè)方法描述符,應(yīng)與BootstrapMethod返回的CallSite實(shí)際相應(yīng)的方法簽名相符;

  • bootstrapMethodHandle:一個(gè)Handle實(shí)例,提供的信息與MethodHandle相似,指定了BootstrapMethod具體實(shí)現(xiàn)的位置;

  • bootstrapMethodArguments:傳入BootstrapMethod的附加參數(shù),可以為Integer、Float、LongDouble、String、org.objectweb.asm.Typeorg.objectweb.asm.Handle幾種類型。真正傳入BootstrapMethod時(shí)基本類型的封裝類會(huì)被拆箱為基本類型,而ASM提供的TypeHandle類分別會(huì)被轉(zhuǎn)換為包含同樣信息的MethodTypeMethodHandle實(shí)例。Java 11和ASM 7.0之后也可以傳入org.objectweb.asm.ConstantDynamic來指定一個(gè)在運(yùn)行時(shí)動(dòng)態(tài)獲取的常量,它的值在調(diào)用BootstrapMethod時(shí)會(huì)被計(jì)算出并作為參數(shù)傳入其中。

    其中Handle類是ASM提供的用于記錄MethodHandle實(shí)例屬性的一個(gè)類,可以通過以下構(gòu)造器獲?。?/p>


  • tag:用于描述該Handle類型的一個(gè)數(shù)學(xué),決定了其對(duì)應(yīng)的MethodHandle的行為,可以將ASM庫(kù)中Opcodes接口中名為的H_XXX字段(如Opcodes.H_GETFIELD)傳入,在對(duì)JVM有所了解的前提下從名稱分析其含義還是比較簡(jiǎn)單的。

  • owner:包含目標(biāo)成員的類的內(nèi)部名稱。

  • name:目標(biāo)成員的名稱。

  • descriptor:描述該invokedynamic指令行為的方法描述符。

  • isInterface:包含目標(biāo)成員的類是否是接口。

Tree API

Tree API中的InvokeDynamicInsnNode對(duì)應(yīng)一個(gè)invokedynamic指令,使用方法與Core API相似,此處不再贅述。

invokedynamic指令的應(yīng)用

在Java語(yǔ)言中invokedynamic指令兩個(gè)最常見個(gè)用途是實(shí)現(xiàn)Lambda表達(dá)式與方法引用,具體的實(shí)現(xiàn)方式超出了本文的范圍,本文中不再詳述。

此處我們真正要探討的是invokedynamic自身的應(yīng)用。 舉個(gè)例子,假設(shè)一個(gè)應(yīng)用程序需要從一個(gè)配置文件中獲取真正的Main類,那么這個(gè)應(yīng)用的入口類可以用反射這樣實(shí)現(xiàn):

如果不使用反射呢?我們也可以生成一個(gè)使用invokedynamic的入口類! 可以使用以下代碼生成main()方法的字節(jié)碼:

這時(shí),可以這樣實(shí)現(xiàn)BootstrapMethod

或許這個(gè)例子有些牽強(qiáng),但這確實(shí)在一個(gè)簡(jiǎn)單的情景下為我們展示了invokedynamic指令的基本用法。

另一個(gè)比較接近實(shí)際的例子是自己在上個(gè)月做的AccessingPath編譯器中實(shí)現(xiàn)的使用字節(jié)碼訪問私有成員的功能。因?yàn)橹苯邮褂梅瓷涞男阅茌^低,那里使用了invokedynamic來訪問私有字段與方法。具體實(shí)現(xiàn)可以在?https://github.com/lovexyn0827/MessMod/tree/master/src/main/java/lovexyn0827/mess/util/access?CompiledPathBytecodeHelper.addInvoker()下找到。



invokedynamic指令的個(gè)人理解的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
女性| 威远县| 宜兰市| 金秀| 鸡西市| 邵东县| 平乐县| 甘孜| 潞城市| 南和县| 康平县| 射洪县| 卓资县| 玛多县| 沙洋县| 唐河县| 鲁甸县| 安新县| 峨边| 会昌县| 玉树县| 平湖市| 山阴县| 康定县| 景泰县| 北辰区| 兴城市| 湖口县| 济南市| 濮阳县| 思南县| 饶平县| 淳安县| 绵阳市| 沂源县| 辛集市| 边坝县| 荆门市| 庐江县| 敦化市| 沂南县|