java 支持动态语言_探秘Java 7:JVM动态语言支持详解

JDK 7 增加了对 JSR 292 的支持,在 JVM 中动态类型语言的运行速度将变得更快。这一支持的关键在于增加了新的 Java 字节码,invokedynamic,它用于方法调用,还有新的连接机制,其中包含了一个新的构造:方法句柄(method handle)。此次JDK 7在动态语言支持上的更新是Java平台发展的重要趋势之一,在今年6月的JavaOne大会上,这些基于Java平台的动态语言也十分的活跃。

动态类型语言和 JVM

JVM 可以执行 Java 程序,将其编译机器独立的字节码。事实上,任何可以使用有效 class 文件表述的功能性语言,都可以运行在 JVM 上。

多年来,运作在 JVM 上语言一直在增加,从 armed Bear for Common Lisp 到Yoix.动态语言的 JVM 实现也越来越多,比如 JRuby 和 Jython,以及 Groovy 脚本语言。

动态语言的灵活性,尤其是脚本语言,对于实验性、原型应用程序以及需频繁更新的程序,都具有独特的吸引力。这种灵活性源自动态类型。动态类型语言中运行时(runtime)验证程序中的值是否与预期类型一致,相对的,静态类型语言,如 Java,是在编译期间检查变量类型,而不是值类型。值得一提的是,Java 平台上另一个前景很被看好的静态语言就是Scala:包括Java之父和Groovy创始人在内的很多开发者都很看好Scala这个强类型的、可扩展性良好的静态语言。

通常,动态类型比静态类型更具灵活性,因为前者允许程序根据运行时的数据生成类型。不过静态类型语言的执行更为高效,因为它能够在编译期间排除错误。

动态类型固有的灵活性与 JVM 的执行效率,合二为一。很明显,这就是它能够吸引动态编程语言创建者以及使用这些语言构建应用程序的开发者的原因。

JSR 223  动态语言支持的第一步

JSR 223: Scripting for the Java Platform 是将动态语言引入 JVM 的第一步,它是一个规范,定义了从动态脚本语言代码访问 Java 代码的 API 接口。它还指定了一个 framework 框架,用户在 Java 应用程序中运行脚本引擎。该规范及其实现使得包含 Java 和脚本代码的应用程序的创建更为容易。

动态类型语言的问题

为运行在 JVM 上的动态类型语言开发引擎,必须满足 JVM 所执行的 Java 字节码的要求,而字节码专为静态类型语言设计。对于引擎开发者,当生成字节码用于方法调用,这种设计一直都是棘手的难点。

方法调用的字节码要求

静态类型语言中编译时进行类型检查,意味着方法调用,以及它生成的字节码,需要知道该方法返回的值类型,以及调用中指定的参数类型。

下面为一段 Java 代码:

String s = "Hello World";

System.out.println(s);

这里参数类型是已知的。System.out.println()并不返回值,如果方法返回值,需要指定返回值的类型。

以上代码相应的字节码如下:

ldc #2

astore_1

getstatic #3   aload_1 invokevirtual #4 // Method java/io/PrintStream.println:(I)V

JVM 中字节码的执行通常包含对操作对象栈(operand stack)中值的操作。操作栈是一个相当于硬件寄存器的虚拟机。通常,字节码会指示 JVM 局部值压入操作对象栈,将值从栈中取出放进局部变量中,复制或交换栈中的值,或者执行生成或使用值的操作。

请看 invokevirtual 一行,它调用了一个方法,而不是对操作对象栈进行操作。从该行注释,我们可以看到,它指出了以下信息:

◆提供方法的接收器(receiver)类:java.io.PrintStream

◆方法名称:println

◆方法参数类型:(I) 表示 Integer

◆方法返回值: V 表示 void

这些信息相当于方法的签名。JVM 查找具有该签名的方法,在这里,就是 java.io.PrintStream 类中的 println:(I)V.如果该方法不在那个类中,JVM 将在类的子类中继续查找。

满足要求所进行的拙劣尝试

为了让动态类型语言满足字节码对方法调用的要求,已经有了多种尝试,但没有一种是理想的。

以下面的代码为例:

function max (x,y) {

if x.lessThan(y) then y else x

}

接收器和参数都没有指定类型,而对于动态类型语言,直到运行时才提供类型信息,因此,以上代码未能满足方法调用需提前获悉类型的要求,也就不能在 Java 平台上成功地编译为字节码。

问题的解决方法之一是为返回值和方法参数创建虚假的(synthetic)Java 类型。在这里,虚假表示非真实存在。例如,动态类型语言在实现是可能将代码更改为:

Interface50 function max (Interface 51 x,Interface52 y) {

if x.lessThan(y) then y else x

}

类型 Interface 51 和 Interface52 并不存在,只是为了满足相应的要求而指定。

另一种方法成为映射调用(relfected invocation),使用 java.lang.reflect.Method 对象调用方法,而避开直接调用方法。这样也就绕开了指定类型的要求。

第三种方法是为动态语言的实现创建一个独特的方法调用解释器(interpreter),以运行在 JVM 上。

虚假类型满足了 Java 字节码的要求。但这种方法不但繁复而且会带来问题。如果动态语言引擎需要更改,相应的实现器(implementer)必须重新创建虚假 Java 类型,这种操作常会出错。

运行调用也有其自身的局限。例如,java.lang.reflect.Method 对象提供了动态语言所需的方法访问,但对象必须是运行时可用的特定 Java 类型。虽然,动态语言可以在运行期间提供类型信息,但不是都可以通过用户映射的规范 Java 类型。

JSR 292 —— 动态语言支持的下一步

JSR 292 为 JVM 引入了一个新的 Java 字节码指令,invokedynamic,以及一个新的方法连接机制。

方法调用的字节码指令

Java 虚拟机规范指定了 4 个字节码,用于方法调用:

◆invokevirtual

◆invokeinterface

◆invokestatic

◆invokespecial

新的 invokedynamic 指令

新的 invokedynamic 字节码指令的语法与 invokeinterface 指令类似:

invokedynamic < method-specification> < n>

但,它的 < method-specification> 只需指定方法名称,对描述符的唯一要求是它应引用非空对象。

invokeinterface 字节码指令差不多是这样的:

invokedynamic #10;

//DynamicMethod java/lang/Object.lessThan:(Ljava/lang/Object;)    重要的是,invokedynamic 字节码指令运行动态语言的实现器(implementer)将方法调用编译为字节码,而不必指定目标的类型,该目标包含了方法、调用的返回类型或方法参数类型。这些类型对于执行指令的 JVM 不必是已知的。但如果未提供接收器的类型,JVM 如何找到该方法?毕竟,JVM 需要连接并调用真实类型上的真实方法。答案在于,JSR 292 还包含了一个新的动态类型语言的连接机制。JVM 使用新的连接机制获取所需的方法。

新的动态连接机制:方法句柄(method handle)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值