Java 8:在新的Nashorn JS引擎中编译Lambda表达式
在最近的一篇文章中,我了解了Java 8和Scala如何实现Lambda表达式。 众所周知,Java 8不仅引入了对Javac编译器的改进,而且还引入了全新的解决方案-Nashorn。
这个新引擎旨在替代Java现有JavaScript解释器Rhino。 这为我们带来了JVM的前列,当谈到在速度与世界的V8引擎执行JavaScript,在那里(我希望我们会最终得到过去那种汽车地毯的事情 :))所以,我认为这将是一个很好的时机,也可以通过深入了解Nashorn,看看它如何编译Lambda表达式(尤其是与Java和Scala相比)。
我们将要看的lambda表达式类似于我们用Java和Scala测试过的表达式。
这是代码:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");String js;js = "var map = Array.prototype.map \n";
js += "var names = [\"john\", \"jerry\", \"bob\"]\n";
js += "var a = map.call(names, function(name) { return name.length() })\n";
js += "print(a)";engine.eval(js);
您似乎天真无邪。 但是,请稍等...
进入字节码
我们的第一个挑战是获取JVM看到的实际字节码。 与Java和Scala的编译器具有持久性(即将.class / jar文件生成到磁盘)不同,Nashorn会编译内存中的所有内容,并将字节码直接传递给JVM。 幸运的是,我们已经有了Java代理来进行救援。 我编写了一个简单的Java代理来捕获并保留生成的字节码。 从那里开始,有一个简单的javap可以打印代码。
如果您还记得的话,我很高兴看到新的Java 8编译器如何使用Java 7中引入的invokeDynamic指令链接到Lambda函数代码。 好吧,与Nashorn一起,他们真的参加了比赛。 现在,一切都完全基于它。 看看下面。
读取字节码
invokeDynamic 。 就像我们都在同一页面上一样,Java 7中添加了invokeDynamic指令,以允许人们编写自己的动态语言来在运行时决定如何链接代码。
对于Java和Scala之类的静态语言,编译器在编译时决定要调用哪种方法(在JVM运行时的帮助下,用于多态)。 运行时链接是通过标准ClassLoader完成的,以查找类。 甚至方法重载解析之类的事情都在编译时完成。
动态与静态链接 。 不幸的是,对于本质上更具动态性的语言(并且JS是一个很好的例子),静态解析可能是不可能的。 当我们在Java中说obj.foo()时,obj类要么具有foo()方法,要么没有。 在像JS这样的语言中,它将依赖于运行时obj引用的实际对象,这是静态编译器的噩梦。 在这种情况下,编译时的链接方法行不通。 但是invokeDynamic确实可以。
InvokeDynamic允许在运行时将链接推迟回该语言的编写者,因此他们可以根据自己的语言语义来指导JVM确定要调用哪种方法。 这是双赢的局面。 JVM获得了一种链接,优化和执行的实际方法,并且语言制造商可以控制其解析度。 动态链接是我们在Takipi中必须努力支持的工作 。
Nashorn如何链接
Nashorn确实有效地利用了这一点。 让我们看一下示例,以了解其工作原理。 这是Lambda代码中的第一条invokeDynamic指令,用于检索JS Array类的值–
invokedynamic 0 "dyn:getProp|getElem|getMethod:prototype":(Ljava/lang/Object;)Ljava/lang/Object;
Nashorn要求JVM在运行时将其传递给该字符串,作为交换,它将向一个接受对象并返回一个对象的方法返回一个句柄。 只要JVM获得了这种方法的句柄,它就可以链接。
.class文件的特殊部分中指定了负责返回此句柄的方法(也称为引导程序方法),该文件中包含可用的引导程序方法列表。 您看到的0值是该方法在该表中的索引,JVM将调用该索引以获取将链接到的方法句柄。
在我看来,Nashorn的人们做了一件很酷的事情,他们没有编写自己的库来解析和链接代码, 而是继续集成了dynalink这个开源项目,该项目旨在帮助动态语言基于统一平台链接代码。 这就是为什么您在每个字符串的开头看到“ dyn:”前缀的原因。
实际流量
现在我们已经掌握了Nashorn所使用的方法,现在让我们看一下实际流程。 为了简洁起见,我删除了一些说明。 完整的代码可以在这里找到。
1.第一组指令将数组映射函数加载到脚本中。
//load JS array
invokedynamic 0 "dyn:getProp|getElem|getMethod:Array":(Ljava/lang/Object;)Ljava/lang/Object;//load its prototype element
invokedynamic 0 "dyn:getProp|getElem|getMethod:prototype":(Ljava/lang/Object;)Ljava/lang/Object;//load the map method
invokedynamic 0 "dyn:getProp|getElem|getMethod:map":(Ljava/lang/Object;)Ljava/lang/Object;//set it to the map local
invokedynamic 0 #0:"dyn:setProp|setElem:map":(Ljava/lang/Object;Ljava/lang/Object;)V
2.接下来,我们分配名称数组
//allocate the names array as a JS object
invokestatic jdk/nashorn/internal/objects/Global.allocate:([Ljava/lang/Object;)Ljdk/nashorn/internal/objects/NativeArray;//places it into names
invokedynamic 0 #0:"dyn:setProp|setElem:names":(Ljava/lang/Object;Ljava/lang/Object;)Vinvokedynamic 0 #0:"dyn:getProp|getElem|getMethod:names":(Ljava/lang/Object;)Ljava/lang/Object;
3.查找并加载Lambda函数
//load the constants field for this script compiled and filled at runtime by Nashorn
getstatic constants//refer to the 2nd entry, where Nashorn will place a handle to the lambda code
iconst_2//get it from the constants array
aaload//ensure it’s a JS function object
checkcast class jdk/nashorn/internal/runtime/RecompilableScriptFunctionData
4.使用名称和Lambda调用map,然后将结果放入
//call the map function, passing it names and the Lambda function from the stackinvokedynamic 0 #1:"dyn:call":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljdk/nashorn/internal/runtime/ScriptFunction ;)Ljava/lang/Object;//put the result in a
invokedynamic 0 #0:"dyn:setProp|setElem:a":(Ljava/lang/Object;Ljava/lang/Object;)V
5.找到打印功能并在打印机上调用
//load the print function
invokedynamic 0 #0:"dyn:getMethod|getProp|getElem:print":(Ljava/lang/Object;)Ljava/lang/Object;//load a
invokedynamic 0 #0:"dyn:getProp|getElem|getMethod:a":(Ljava/lang/Object;)Ljava/lang/Object;// call print on it
invokedynamic 0 #2:"dyn:call":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
lambda函数本身被编译并作为私有函数放在与脚本相同的类中。 这与Java 8 lambda非常相似。 代码本身很简单。 我们加载字符串,找到其长度函数并调用它。
//Load the name argument (var #1)
aload_1//find its length() function
invokedynamic 0 "dyn:getMethod|getProp|getElem:length":(Ljava/lang/Object;)Ljava/lang/Object;//call length
invokedynamic 0 "dyn:call":(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;//return the result
areturn
奖金回合–最终的字节码
到目前为止,我们一直在处理的代码实际上并不是JVM在运行时将执行的代码。 请记住,每个invokeDynamic指令都将解析为物理字节码方法,然后JVM将其编译为机器代码并执行。
为了查看JVM运行的实际字节码,我使用了一个简单的技巧。 我在类中使用简单的Java方法调用包装了对length()的调用。 这使我可以放置一个断点,并查看JVM执行以进入Lambda的最终调用堆栈。
这是代码–
js += "var a = map.call(names, function(name) {
return Java.type("LambdaTest”).wrap(name.length())
})";
包好了–
public static int wrap(String s)
{
return s.length();
}
现在玩游戏。 该堆栈中有几帧? 想一想。 如果您猜不到<100 –您欠我一杯啤酒。 完整的调用堆栈可以在这里找到。
之所以如此,也是非常有趣的原因,这是关于即将发布的全新帖子的故事。
翻译自: https://www.javacodegeeks.com/2014/02/java-8-compiling-lambda-expressions-in-the-new-nashorn-js-engine.html
Java 8:在新的Nashorn JS引擎中编译Lambda表达式相关推荐
- nashorn预编译_Java 8:在新的Nashorn JS引擎中编译Lambda表达式
nashorn预编译 在最近的一篇文章中,我了解了Java 8和Scala如何实现Lambda表达式. 众所周知,Java 8不仅引入了对Javac编译器的改进,而且还引入了全新的解决方案-Nasho ...
- 项目中使用 java函数式编程_函数式编程在Java8中使用Lambda表达式进行开发
事情起因 如果不喜欢看故事的同学,请前往下一章节. 事情起因是我打算集成Redis缓存,配置了Redis以后,发现@Cacheable注解始终不生效,折腾了大半天以后,我的心态已经崩了,决定不使用@C ...
- Java新特性:Java8函数式接口与Lambda表达式(一)
摘要 何为函数式接口? 什么是lambda表达式,lambda表达式的本质: 函数式接口与lambda表达式的联系:lambda是实现函数式接口的一个快捷方式,可以作为函数式接口的一个实例: 常用Ja ...
- 使用NetBeans Lambda支持在Java 8中使用Lambda表达式对列表进行排序
作为JSR 335的一部分, Lambda表达式已从Java 8开始引入Java语言,这是Java语言的一个重大变化. 如果您想了解更多关于Lambda表达式以及JSR 335的信息,可以访问以下资源 ...
- 如何开始使用Java中的Lambda表达式
by Luis Santiago 路易斯·圣地亚哥(Luis Santiago) 如何开始使用Java中的Lambda表达式 (How to start working with Lambda Exp ...
- 如何在Java中使用Lambda表达式
如何在Java中使用Lambda表达式 Lambda表达式是Java 8新增的一个非常强大的特性.它使得函数式编程在Java中变得更加容易和直观. Lambda表达式的基础知识 在Java中,Lamb ...
- Java学习记录五(多线程、网络编程、Lambda表达式和接口组成更新)
Java学习记录五(多线程.网络编程.Lambda表达式和接口组成更新) Java 25.多线程 25.1实现多线程 25.1.1进程 25.1.2线程 25.1.3多线程的实现 25.1.4设置和获 ...
- Java 8中使用Lambda表达式的策略模式
策略模式是" 设计模式:可重用对象的元素"书中的模式之一 . 本书所述的策略模式的意图是: 定义一系列算法,封装每个算法,并使它们可互换. 策略使算法独立于使用该算法的客户端而变化 ...
- 探索Java语言与JVM中的Lambda表达式
2019独角兽企业重金招聘Python工程师标准>>> 转载来源:http://www.admin10000.com/document/1291.html Lambda表达式是自Ja ...
最新文章
- TCP网络应用程序的注意点
- 关于 performSelector 的一些小探讨
- 信息提醒之Toast-更新中
- 一堂儿童科学实验课引起的思考:数学和化学有什么关系?
- leetcode491. 递增子序列(回溯算法)
- webpack filemanager-webpack-plugin 打多个zip时,出现循环打包情况
- C#在类型实例化时都干了什么:从一道笔试题说开去
- onpostexecute 中更新adapter 事变_Mac音频转换器----Adapter
- 《人月神话》读后感一
- python发送cookie请求_Python中实现带Cookie的Http的Post请求
- Thinkpad官方推荐的驱动安装顺序
- 段码液晶屏实现原理和需要注意的事项
- 内存分配函数总结- 陈敏的博客- CSDN博客
- 从“傻X”到牛X——写在最前面的话
- 短信定位 android,短信定位
- 网络篇 物理拓扑与逻辑拓扑-19
- 西南大学计算机学院读博咋样,专硕想读博士,难吗?北大学长如是说!
- 查看Python的安装目录
- Discuz首页最后发表帖子地址伪静态
- 区块链零售市场价值到2023年将飙升29倍
热门文章
- MyBatisPlus(笔记)
- OpenCV3.4.5带GPU编译error: #error This file requires compiler and library support for the ISO C++ 2011
- python快捷方式图标_python – PyInstaller无法更改快捷方式图标
- 车提示检测轮胎气压_水淹车估价中心_辽宁中车检
- MySQL优化(四):count()
- 注解代替枚举_精选枚举代替开关
- feign rest_与Feign客户轻松进行REST通信
- 带有Prometheus的弹簧靴和千分尺第5部分:旋转Prometheus
- Spring Boot应用程序浪费了内存
- 回调函数中有回调函数吗_嗨,那里有回调!