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.mzph.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 会在执行你的命令之前对输入 …

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…

lfu算法实现c语言_哈希查找算法(C语言实现)

上一节介绍了有关哈希表及其构造过程的相关知识&#xff0c;本节将介绍如何利用哈希表实现查找操作。在哈希表中进行查找的操作同哈希表的构建过程类似&#xff0c;其具体实现思路为&#xff1a;对于给定的关键字 K&#xff0c;将其带入哈希函数中&#xff0c;求得与该关键字对…

现代化Java代码的七个NetBeans提示

在“ 七个不可或缺的NetBeans Java提示”一文中 &#xff0c;我谈到了一般使用NetBeans提示的问题&#xff0c;然后重点介绍了七个提示。 接下来列出了该帖子中强调的七个提示&#xff1a; 可疑方法调用 使用或&#xff01; AND字符串构造函数比较字符串 构造函数中的可重写方…

手机屏幕宽高像素计算_2020年的智能手机拍照新设计,就全看下半年了

此前我们三易生活在总结2019年智能手机影像设计的变化时曾经提到&#xff0c;智能手机上的大底与大像素设计几乎是花了一整年的时间&#xff0c;把自己从单纯的噱头&#xff0c;逐步改进成了能够确实提高拍照画质的业界主流技术。站在现在的视角来看&#xff0c;2019年这阵“高…

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;为其授予访问权限…

MongoDB学习1——Windows 下配置及启动mongodb服务器

1.下载mongodb程序&#xff1a;http://lt1.cr173.com/soft2/mongodb.zip 2.解压程序3.在解压后的mongodb文件夹中创建 data、logs、conf文件夹4.编写配置文件conf\mongod.conf#端口号 port 12345 #数据库路径&#xff0c;也可以使用绝对路径 dbpath data #日志路径&#xff0…

JavaFX 2.0条形图和散点图(以及JavaFX 2.1 StackedBarCharts)

JavaFX 2.0提供了用于生成图表的内置功能&#xff0c;该功能可在javafx.scene.chart包中找到。 在本文中&#xff0c;我将介绍如何使用JavaFX 2.0创建条形图和散点图 。 在本文的学习过程中&#xff0c;我将一路使用Guava和一些Java 7功能。 在演示JavaFX 2.0图表API之前&#…

python中下划线开头的命名_Python 中各种下划线的骚操作:_、_xx、xx_、__xx、__xx__、_classname_...

我们在定义一些变量或者方法的时候&#xff0c;常常会用到下划线&#xff0c;在 Python 中&#xff0c;下划线可是很有用处的哟&#xff0c;比如变量&#xff0c;有些是一个下划线开头的(_xx)&#xff0c;有些是两个下划线开头的(__xx)&#xff0c;有些是在名称的结尾添加下划线…

MongoDB学习2——Windows 使用mongo连接数据库

一、查看mongo帮助文档mongo.exe --help二、使用mongo 连接服务器mongo.exe 数据库地址&#xff1a;数据库端口号/数据库关闭服务器db.shutdownServer()注&#xff1a;关闭数据必须使用admin数据库权限

【UVA 10816】 Travel in Desert (最小瓶颈树+最短路)

【题意】 有n个绿洲&#xff0c; m条道路&#xff0c;每条路上有一个温度&#xff0c;和一个路程长度&#xff0c;从绿洲s到绿洲t&#xff0c;求一条道路的最高温度尽量小&#xff0c; 如果有多条&#xff0c; 选一条总路程最短的。 InputInput consists of several test cases…

联想小新air14笔记本黑屏_联想小新air14锐龙版测评,谈谈它的好和坏

联想小新air14锐龙版本测评了解数码就找小侠客&#xff0c;我是机圈小侠客 今天呢&#xff0c;主要和大家测评一下联想小新air14这款笔记本&#xff0c;总体而言的话&#xff0c;这款笔记本它是一个。对于办公人士或者轻度游戏爱好者来说的话&#xff0c;是一个不错的选择&…

Java入门:Java下载与安装方法

本文适合刚入门的Java编程的初学者阅读。 JDK有两种下载方法&#xff0c;一个是官网下载&#xff0c;另一个是第三方网站下载。官网速度也许有点慢&#xff0c;慢的话可以考虑去第三方网站下载。 一、官网下载 1. 访问地址&#xff1a;http://www.oracle.com/cn/downloads/inde…

Java 7:如何编写非常快速的Java代码

当我第一次写此博客时&#xff0c;我的目的是向您介绍ThreadLocalRandom类&#xff0c;它是Java 7中新增的用于生成随机数的类。 我已在一系列微基准测试中分析了ThreadLocalRandom的性能&#xff0c;以了解其在单线程环境中的性能。 结果相对令人惊讶&#xff1a;尽管代码非常…

[地图开发][算法及数据结构]四叉树原理

参考&#xff1a;http://blog.csdn.net/zhouxuguang236/article/details/12312099 原博客地址还有c&#xff0b;&#xff0b;源码。。。 四叉树索引的基本思想是将地理空间递归划分为不同层次的树结构。它将已知范围的空间等分成四个相等的子空间&#xff0c;如此递归下去&…

按键 粘贴上一个命令_合并单元格、选择性粘贴的快捷键都是啥?今天一次告诉你……...

经常有人在群里问&#xff0c;合并单元格的快捷键是什么&#xff1f;选择性粘贴数值的快捷键是什么&#xff1f;今天就来聊聊快捷键的一些冷门知识……Alt键的作用快捷键其实就是一些组合键&#xff0c;主要用到Ctrl、shift、Alt这三个键其中之一或者是几个&#xff0c;再加上其…

Spring MVC和JQuery用于Ajax表单验证

在本教程中&#xff0c;我们将看到如何使用Ajax和Spring MVC和JQuery在服务器端验证表单。 Spring MVC为通过注释驱动的配置采用Ajax提供了非常方便的过程。 我们将使用此注释驱动的配置以JSON数据的形式发送Ajax响应。 响应将包含表单验证的状态&#xff0c;并且表单数据中存在…