Java 7:完整的invokedynamic示例

我當前的Java 7系列中的另一個博客條目。 這次,它處理的是invokedynamic,這是JVM上用于方法調用的新字節碼指令。 invokedynamic指令允許在呼叫站點和呼叫接收者之間進行動態鏈接。

這意味著您可以將正在執行方法調用的類鏈接到在運行時正在接收調用的類(和方法)。 所有其他用于方法調用的JVM字節碼指令,例如invokevirtual ,都將目標類型信息硬連線到編譯中,即硬連線到類文件中。 讓我們看一個例子。

Constant pool:#1 = Class              #2             //  com/schlimm/bytecode/examples/BytecodeExamples...#42 = Class              #43            //  java/lang/String...#65 = Methodref          #42.#66        //  java/lang/String.length:()I#66 = NameAndType        #67:#68        //  length:()I#67 = Utf8               length#68 = Utf8               ()I...
{...public void virtualMethodCall();flags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: ldc           #44                 // String Hello2: invokevirtual #65                 // Method java/lang/String.length:()I5: pop6: returnLineNumberTable:line 31: 0line 32: 6LocalVariableTable:Start  Length  Slot  Name   Signature0       7     0  this   Lcom/schlimm/bytecode/examples/BytecodeExamples;
}

上面的字節碼片段顯示了java.lang的invokevirtual方法調用。 第20行中的String- > length() 。它引用了contsant池表中的Item 65,它是MethodRef條目(請參見第6行)。 常量池表中的項目42和66引用類和方法描述符條目。 如您所見,invokevirtual調用的目標類型和方法已完全解析,并硬連接到字節碼中。 現在,讓我們回到invokedynamic

重要的是要注意,不可能將Java代碼編譯為包含invokedynamic指令的字節碼。 Java是靜態類型的 。 這意味著Java在編譯時執行類型檢查。 因此,在Java中,有可能(并且想要!)將方法調用接收者的所有類型信息硬連線到調用者類文件中。 調用方知道調用目標的類型名稱,如上面的示例所示。 另一方面,使用invokedynamic可使JVM在運行時準確解析該類型信息。 只有動態語言(例如JRuby或Rhino)才需要(也想要!)。

現在,假設您要在動態鍵入的JVM上實現一種新語言。 我不建議您在JVM上發明另一種語言,但是假設您應該,并且假設您的新語言應被動態鍵入。 用您的新語言,這意味著在運行時執行方法調用的調用方和接收方之間的鏈接。 由于Java 7,這可以使用invokedynamic指令在字節碼級別上實現。

因為無法使用Java編譯器創建invokedynamic指令,所以我將創建一個包含我自己的invokedynamic的類文件。 創建此類文件后,我將使用普通的Java啟動器運行該類文件的main方法。 沒有編譯器,如何創建類文件? 這可以通過使用字節碼操作框架(例如ASM或Javassist)來實現 。

以下代碼段顯示了SimpleDynamicInvokerGenerator ,該生成器可以生成一個類文件SimpleDynamicInvoker.class,該文件包含invokedynamic指令。

public abstract class AbstractDynamicInvokerGenerator implements Opcodes {public byte[] dump(String dynamicInvokerClassName, String dynamicLinkageClassName, String bootstrapMethodName, String targetMethodDescriptor)throws Exception {ClassWriter cw = new ClassWriter(0);FieldVisitor fv;MethodVisitor mv;AnnotationVisitor av0;cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, dynamicInvokerClassName, null, "java/lang/Object", null);{mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);mv.visitCode();mv.visitVarInsn(ALOAD, 0);mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");mv.visitInsn(RETURN);mv.visitMaxs(1, 1);mv.visitEnd();}{mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);mv.visitCode();MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,MethodType.class);Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, dynamicLinkageClassName, bootstrapMethodName,mt.toMethodDescriptorString());int maxStackSize = addMethodParameters(mv);mv.visitInvokeDynamicInsn("runCalculation", targetMethodDescriptor, bootstrap);mv.visitInsn(RETURN);mv.visitMaxs(maxStackSize, 1);mv.visitEnd();}cw.visitEnd();return cw.toByteArray();}protected abstract int addMethodParameters(MethodVisitor mv);}public class SimpleDynamicInvokerGenerator extends AbstractDynamicInvokerGenerator {@Overrideprotected int addMethodParameters(MethodVisitor mv) {return 0;}public static void main(String[] args) throws IOException, Exception {String dynamicInvokerClassName = "com/schlimm/bytecode/SimpleDynamicInvoker";FileOutputStream fos = new FileOutputStream(new File("target/classes/" + dynamicInvokerClassName + ".class"));fos.write(new SimpleDynamicInvokerGenerator().dump(dynamicInvokerClassName, "com/schlimm/bytecode/invokedynamic/linkageclasses/SimpleDynamicLinkageExample", "bootstrapDynamic", "()V"));}}

我在這里使用ASM (一種通用的Java字節碼操縱和分析框架)來完成創建正確的類文件格式的工作。 在第30行中, visitInvokeDynamicInsn創建了invokedynamic指令。 生成一個進行invokedynamic調用的類只是故事的一半。 您還需要一些將動態調用站點鏈接到實際目標的代碼,這是invokedynamic的真正目的。 這是一個例子。

public class SimpleDynamicLinkageExample {private static MethodHandle sayHello;private static void sayHello() {System.out.println("There we go!");}public static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {MethodHandles.Lookup lookup = MethodHandles.lookup();Class thisClass = lookup.lookupClass(); // (who am I?)sayHello = lookup.findStatic(thisClass, "sayHello", MethodType.methodType(void.class));return new ConstantCallSite(sayHello.asType(type));}}

第9-14行中的bootstrap方法選擇動態調用的實際目標。 在我們的例子中,目標是sayHello ()方法。 要了解bootstrap方法如何鏈接到invokedynamic指令,我們需要深入研究使用SimpleDynamicInvokerGenerator生成的SimpleDynamicInvoker字節碼。

E:\dev_home\repositories\git\playground\bytecode-playground\target\classes\com\schlimm\bytecode>javap -c -verbose SimpleDynamicInvoker.classClassfile /E:/dev_home/repositories/git/playground/bytecode-playground/target/classes/com/schlimm/bytecode/SimpleDynamicInvoker.classLast modified 30.01.2012; size 512 bytesMD5 checksum 401a0604146e2e95f9563e7d9f9d861b
public class com.schlimm.bytecode.SimpleDynamicInvokerBootstrapMethods:0: #17 invokestatic com/schlimm/bytecode/invokedynamic/linkageclasses/SimpleDynamicLinkageExample.bootstrapDynamic:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;Method arguments:minor version: 0major version: 51flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Utf8               com/schlimm/bytecode/SimpleDynamicInvoker#2 = Class              #1             //  com/schlimm/bytecode/SimpleDynamicInvoker#3 = Utf8               java/lang/Object#4 = Class              #3             //  java/lang/Object#5 = Utf8               <init>#6 = Utf8               ()V#7 = NameAndType        #5:#6          //  "<init>":()V#8 = Methodref          #4.#7          //  java/lang/Object."<init>":()V#9 = Utf8               main#10 = Utf8               ([Ljava/lang/String;)V#11 = Utf8               com/schlimm/bytecode/invokedynamic/linkageclasses/SimpleDynamicLinkageExample#12 = Class              #11            //  com/schlimm/bytecode/invokedynamic/linkageclasses/SimpleDynamicLinkageExample#13 = Utf8               bootstrapDynamic#14 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;#15 = NameAndType        #13:#14        //  bootstrapDynamic:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;#16 = Methodref          #12.#15        //  com/schlimm/bytecode/invokedynamic/linkageclasses/SimpleDynamicLinkageExample.bootstrapDynamic:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;#17 = MethodHandle       #6:#16         //  invokestatic com/schlimm/bytecode/invokedynamic/linkageclasses/SimpleDynamicLinkageExample.bootstrapDynamic:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;#18 = Utf8               runCalculation#19 = NameAndType        #18:#6         //  runCalculation:()V#20 = InvokeDynamic      #0:#19         //  #0:runCalculation:()V#21 = Utf8               Code#22 = Utf8               BootstrapMethods
{public com.schlimm.bytecode.SimpleDynamicInvoker();flags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #8                  // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);flags: ACC_PUBLIC, ACC_STATICCode:stack=0, locals=1, args_size=10: invokedynamic #20,  0             // InvokeDynamic #0:runCalculation:()V5: return
}

在第49行中,您可以看到invokedynamic指令。 動態方法的邏輯名稱是runCalculation ,這是一個虛擬名稱。 您可以使用任何有意義的名稱,也可以使用“ +”之類的名稱。 該指令引用了競爭池表中的第20項(請參見第33行)。 這又引用了BootstrapMethods屬性中的索引0(請參見第8行)。 在這里,您可以看到指向SimpleDynamicLinkageExample.bootstrapDynamic方法的鏈接,該方法將invokedynamic指令鏈接到調用目標。

現在,如果您使用java啟動器調用SimpleDynamicInvoker ,則將執行invokedynamic調用。

下面的序列圖說明了使用Java啟動器調用SimpleDynamicInvoker時發生的情況。

使用invokedynamic的runCalculation的第一次調用會發出對bootstrapDynamic方法的調用。 此方法在調用類(SimpleDynamicInvoker)和接收類( SimpleDynamicLinkageExample )之間進行動態鏈接。 bootstrap方法返回一個以接收類為目標的MethodHandle。 緩存此方法句柄以重復調用runCalculation方法。

這就是invokedynamic。 我在Git倉庫中發布了一些更復雜的示例。 希望您在閱讀不足時喜歡閱讀本文!

干杯,尼克拉斯

參考:

  • JCG合作伙伴提供的 “ Java 7:一個完整??的invokedynamic示例” ? 尼克拉斯。
  • http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html
  • http://asm.ow2.org/
  • http://java.sun.com/developer/technicalArticles/DynTypeLang/
  • http://asm.ow2.org/doc/tutorial-asm-2.0.html
  • http://weblogs.java.net/blog/forax/archive/2011/01/07/calling-invokedynamic-java
  • http://nerds-central.blogspot.com/2011/05/performing-dynamicinvoke-from-java-step.html

翻譯自: https://www.javacodegeeks.com/2012/02/java-7-complete-invokedynamic-example.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/373616.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373616.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373616.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

VC6兼容性及打開文件崩潰問題解決

VC6雖然老&#xff0c;但是一些工程還非得用它打開&#xff0c;沒辦法…… 今天偶然用到&#xff0c;因為新裝了系統&#xff0c;之前的問題又要重新解決一遍 在這記錄下解決過程&#xff0c;方便以后查閱&#xff1a; 一.兼容問題&#xff1a; XP以上windows系統打開VC6時可能…

Linux入門筆記——echo

echo Display a line of text(顯示一行文本)這個命令的作用相當簡單明了。傳遞到 echo 命令的任一個參數都會在&#xff08;屏幕上&#xff09;顯示出來。 小插曲&#xff1a; 每當你輸入一個命令&#xff0c;然后按下 enter 鍵后&#xff0c;bash 會在執行你的命令之前對輸入 …

10.25模擬 三角形

1. 三角形(trokuti.cpp/c/pas)? 【 問題描述 】? 平面上有N條直線&#xff0c;用方程A i x B i y C i0表示。這些直線沒有三線共點的。現在要你計算出用這些直線可以構造出多少三角形&#xff1f;【 輸入格式 】? 第1行&#xff1a;一個整數N(1 ≤ N≤ 300000)。? 下面N行…

dataframe 空值替換為0_Python數據分析:Pandas之DataFrame

內容目錄 DataFrame簡介DataFrame創建方式DataFrame索引和切片DataFrame屬性DataFrame級聯與合并DataFrame基本操作DataFrame分組聚合操作DataFrame數據透視與交叉表1 DataFrame簡介 我們在上次課中講到了Pandas的Series結構,還沒看的點這里 ailsa:python數據分析:Pandas之S…

具有NetBeans,WebLogic 12c,JPA和MySQL數據源的Arquillian

您可能已經關注了我的文章&#xff0c;其中涉及使用嵌入式GlassFish測試更復雜的場景&#xff08; 第I部分/第II部分 &#xff09;。 我要做的下一步是使此設置與最新的WebLogic 12c一起使用。 入門 按照我的前兩個帖子的入門部分中的步驟進行操作。 要使此工作正常&#xff…

SQL中 char varchar和nvarchar的區別

轉至&#xff1a;http://www.cnblogs.com/carekee/articles/2094676.html char char是定長的&#xff0c;也就是當你輸入的字符小于你指定的數目時&#xff0c;char(8)&#xff0c;你輸入的字符小于8時&#xff0c;它會再后面補空值。當你輸入的字符大于指定的數時&#xff…

Linux入門筆記——less

less命令是一個用來瀏覽文本文件的程序,是早期 Unix 程序 more 的改進版,屬于”頁面調度器”程序類&#xff0c;該程序通過頁方式在一頁中輕松地文本文檔。然而 more 程序只能向前分頁瀏覽&#xff0c;而 less 程序允許前后分頁瀏覽&#xff0c;它還有很多其它的特性。 less 命…

lfu算法實現c語言_哈希查找算法(C語言實現)

上一節介紹了有關哈希表及其構造過程的相關知識&#xff0c;本節將介紹如何利用哈希表實現查找操作。在哈希表中進行查找的操作同哈希表的構建過程類似&#xff0c;其具體實現思路為&#xff1a;對于給定的關鍵字 K&#xff0c;將其帶入哈希函數中&#xff0c;求得與該關鍵字對…

現代化Java代碼的七個NetBeans提示

在“ 七個不可或缺的NetBeans Java提示”一文中 &#xff0c;我談到了一般使用NetBeans提示的問題&#xff0c;然后重點介紹了七個提示。 接下來列出了該帖子中強調的七個提示&#xff1a; 可疑方法調用 使用或&#xff01; AND字符串構造函數比較字符串 構造函數中的可重寫方…

AngularJS $http service

原文鏈接&#xff1a;http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/angularjs-http-service/ 1.通常用法&#xff1a; $http服務是一個只有一個參數-配置對象的函數&#xff0c;用于產生一個http請求并返回一個promise對象。 $http({ method: GET, url: /someUr…

Erwin 生成 mysql 帶注釋(comment )的腳本

Erwin設計數據庫非常方便&#xff0c;有邏輯視圖和物理視圖&#xff0c;可以很方便的生成數據庫文檔和SQL 腳本。在使用過程中唯一不爽的地方是腳本不能生成comment。在百度無數次無法解決下&#xff0c;又翻墻谷歌&#xff0c;在一個日本網站找到了解決辦法&#xff0c;不敢獨…

YY語音01

1、YYSetup-8.2.0.1-zh-CN.exe (http://waiwai.duowan.com/)(http://www.yy.com/download) C:\Program Files (x86)\duowan\yy 1.1、YY游戲大廳&#xff0c;指向的安目錄&#xff1a;“C:\Users\33\AppData\Roaming\duowan\yygame\ver\0\1.3.1.13\” 2、 3、 4、 5、轉載于:htt…

手機屏幕寬高像素計算_2020年的智能手機拍照新設計,就全看下半年了

此前我們三易生活在總結2019年智能手機影像設計的變化時曾經提到&#xff0c;智能手機上的大底與大像素設計幾乎是花了一整年的時間&#xff0c;把自己從單純的噱頭&#xff0c;逐步改進成了能夠確實提高拍照畫質的業界主流技術。站在現在的視角來看&#xff0c;2019年這陣“高…

帶有Jersey和Spring的RESTful Web應用程序

幾個月前&#xff0c;我們的任務是創建一個API&#xff0c;以向第三方開發人員公開我們系統中的某些功能。 我們選擇將這些功能公開為一系列REST Web服務。 我開始使用Jersey &#xff0c;它是JSR 311 &#xff08;用于Restful Services的Java API&#xff09;的參考實現&#…

關于更新pip的心得

如果pip install --upgrade pip 刪除了自己&#xff0c;但是無法安裝新的自己。 那么下載最新的pip,解壓 1、在命令窗口輸入 python(前提條件已經在系統路徑) setup.py(pip 包里的腳本) install 即可 2、注銷一下&#xff0c;讓pip在系統路徑生效 3、打開cmd&#xff0c;輸…

Linux/Unix/Mac 系統GIT密碼存儲

新建一個 ~/.netrc 文件&#xff0c; 將 git 服務器&#xff0c; 用戶名以及密碼記錄在這個文件&#xff0c; 如下所示&#xff1a; machine your-git-server login your-username password your-password普通用戶的 git-server 填 github.com 就可以了. 如果有多個 server 就…

財務部門:你需要多長時間才能夠回答老板的這些問題?

企業經營管理中&#xff0c;對管理決策者來說&#xff0c;有許多問題是希望隨時都知道的&#xff0c;下面&#xff0c;我們一起來探討一下&#xff0c;這些常見的問題&#xff0c;我們怎么來快速回答。首先&#xff0c;我們來回答以下幾個問題&#xff1a;注&#xff1a;這幾個…

yield方法釋放鎖嗎_死磕Synchronized底層實現重量級鎖

點擊上方“Java知音”&#xff0c;選擇“置頂公眾號”技術文章第一時間送達&#xff01;作者&#xff1a;farmerjohngit鏈接&#xff1a;https://github.com/farmerjohngit本文為死磕Synchronized底層實現第四篇文章&#xff0c;內容為重量級鎖實現。本系列文章將對HotSpot的sy…

Java應用程序上的Twitter API

是否曾想過將推文附加到Java應用程序&#xff1f; 我為此尋找了最好的API&#xff0c;很幸運&#xff0c;我找到了它&#xff01; http://twitter4j.org/ 一個簡單的方法&#xff1a; 我們需要做的第一件事是在您的Twitter帳戶中創建一個應用程序&#xff0c;為其授予訪問權限…

ps aux和ps -ef命令區別

ps aux 是用BSD的格式來顯示 java這個進程顯示的項目有&#xff1a;USER,PID,%CPU,%MEM,VSZ,RSS,TTY,STAT,START,TIME,COMMANDps -ef 是用標準的格式顯示java這個進程顯示的項目有&#xff1a;UID,PID,PPID,C,STIME,TTY,TIME,CMD&#xff09;轉載于:https://www.cnblogs.com/ya…