我当前的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

Java 7:完整的invokedynamic示例相关推荐

  1. java自建ocr完整示例_Java 7:完整的invokedynamic示例

    java自建ocr完整示例 我当前的Java 7系列中的另一个博客条目. 这次它处理的是invokedynamic,这是JVM上用于方法调用的新字节码指令. invokedynamic指令允许呼叫站点 ...

  2. 10个Java 8 Lambda表达式经典示例

    Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表 达式,它将允许我们将行为传到函数里.在J ...

  3. Java异常处理教程(包含示例和最佳实践)

    异常是可能在程序执行期间发生的错误事件,它会破坏其正常流程. Java提供了一种健壮且面向对象的方式来处理异常情况,称为Java异常处理 . 我们将在本教程中研究以下主题. Java异常处理概述 异常 ...

  4. java组合与继承始示例_Java 8特性与示例

    java组合与继承始示例 Java 8 was released on 18th March 2014, so it's high time to look into Java 8 Features. ...

  5. Java TCP 抓包简单示例

    Java TCP 抓包简单示例 由于目前网上没有一篇能真正方便读者操作的此类文章,本文对此通过示例做个简单介绍. 缘起 有一天本来在看头条,然后看到一则游戏的广告,看画面可能是我喜欢的建造类型(纪元1 ...

  6. Java方法完整调用链生成工具

    1. 前言 在很多场景下,如果能够生成Java代码中方法之间的调用链,是很有帮助的,例如分析代码执行流程.确认被修改代码的影响范围.代码审计/漏洞分析等. IDEA提供了显示调用指定Java方法向上的 ...

  7. Java IOUtils.copy方法代码示例(亲测)

    本文整理汇总了Java中org.apache.commons.io.IOUtils.copy方法的典型用法代码示例.如果您正苦于以下问题:Java IOUtils.copy方法的具体用法?Java I ...

  8. 经典十大排序算法(含升序降序,基数排序含负数排序)【Java版完整代码】【建议收藏系列】

    经典十大排序算法[Java版完整代码] 写在前面的话 十大排序算法对比 冒泡排序 快速排序 直接选择排序 堆排序 归并排序 插入排序 希尔排序 计数排序 桶排序 基数排序 完整测试类 写在前面的话   ...

  9. Java中的Volatile如何工作? Java中的volatile关键字示例

    如何在Java中使用Volatile关键字 在Java采访中,什么是volatile变量以及何时在Java中使用volatile变量是Java 采访中一个著名的多线程采访问题 . 尽管许多程序员都知道 ...

最新文章

  1. java arcengine_在Java程序中调用ArcEngine
  2. 系统开出出现问题~~~\WINDOWS\SYSTEM32\CONFIG\SYSTEM 损坏或丢失无法开机
  3. 深入理解javascript选择器API系列第二篇——getElementsByClassName
  4. mfc笔记--摘录关于裁剪窗口区域的设置,WS_CLIPCHILDREN和WS_CLIPSIBLINGS的理解
  5. WebIDE push files to ABAP repository
  6. C++分析使用拷贝控制成员和调用构造函数的时机
  7. Rabbits UVALive - 8211
  8. 【洛谷2624】[HNOI2008] 明明的烦恼(Python+利用prufer序列结论求解)
  9. 2022见证中国崛起从Python绘制中国地图开始:使用pyecharts最新版本绘制中国地图实例详解,个性化地图定制及常用参数解析
  10. 多种语言打印Hello World
  11. 201671030112—词频统计软件项目报告
  12. 《中国人史纲》读书笔记:第六、七章 纪元前八、七世纪
  13. PTA 6-1 单链表逆转
  14. 阿里云建站费用给大家看下速成美站、企业官网、营销建站价格表
  15. 怎么修改数据库服务器名字,修改SQL Server数据库服务器名字
  16. 快速复制粘贴小工具txmouse
  17. IDEA 2021 没有Allow parallel run
  18. Python爬虫的起点,一文轻松入门
  19. Plu2006即将开赛,参赛选手名单公布
  20. HDP vs CDH

热门文章

  1. 生成configDataContextRefres失败:Error creating bean with name ‘configDataContextRefresher‘
  2. 如何导出数据到Excel表格
  3. 大文件拆分小文件求top_将文件拆分为流
  4. wildfly管理控制台_WildFly管理控制台已更新–请求反馈
  5. jaxb报错_JAXB做错了; 尝试Xembly
  6. Java –缺少字体–崩溃的应用程序!
  7. EA问题的JDK14实例
  8. 知识图谱 图数据库 推理_图数据库的知识表示与推理
  9. 参数化测试 junit_参数化的JUnit测试
  10. 更深入地了解Java 8 Date and Time API