技术开发 频道

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

  新的 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)

  JDK 7 包含了新包,java.dyn,其中包含了与在 Java 平台中动态语言支持相关的类。其中一个类为 MethodHandle.方法句柄是类型 java.dyn.MethodHandle 的一个简单对象,该对象包含一个 JVM 方法的匿名引用。

  新连接机制还包含一个引导方法(bootstrap 方法),它是一个方法句柄,决定了调用现场(call site)调用的目标方法。调用现场是调用指令的实例,在本节中,它就是 invokedynamic 字节码指令的实例。包含 invokedynamic 指令的每个类都必须指定引导方法。

  JVM 第一次遇到具有接收器和参数的 invokedynamic 字节码时,它调用引导方法。调用语言支持的方法,可以使用术语 up-call 来描述。

  引导方法反过来选择相应的目标方法句柄。然后 JVM 将该方法句柄引用的方法与 invokedynamic 字节码关联起来。JVM 下次遇到具有相同接收器和参数的 invokedynamic 字节码时,它将立即调用之前所选的方法。

  方法句柄相当简单,仅包含一个描述特定类型的类型令牌(type toke)。此外,方法句柄隐式地包含一个与其关联的 invoke 方法。要调用方法句柄,你需要调用它的 invoke 方法,与调用对象方法类似,即 MethodHandle.invoke(……)。由于每个方法句柄都具有其自身的类型,因此,它只接受那个类型的 invoke 调用。如果调用的类型与方法句柄的类型不匹配,方法句柄将返回异常。 总之,方法句柄提供了一种连接机制,它能够让 JVM 根据 invokedynamic 字节码指令调用正确的方法。但 JVM 遇到 invokedynamic 字节码时,它将使用方法句柄获得所需的方法。请注意,相对于映射调用,方法句柄提供了一种更好的方式,来满足方法调用的字节码要求。相较而言,方法句柄提供了一种命名和连接方法的方式,而无需考虑方法类型或位置,而且这种方式具有完善的类型安全和本地的执行速度。

  通过接口注入(interface injection)在运行时修改类

  接口注入能够在运行时修改类,这样类就可以构建新的接口。对于动态类型语言,尤其是基本语言,这是一个常见的功能。但它不属于 JVM 标准的一部分。该功能还处于调研阶段,以便加入 JSR 292 中。

  在 JVM 中支持接口注入,运行时语言将可以推荐新的功能,以模块化的方式供其自身使用。例如,假设 JVM 在运行的语言的类或类集合需要串行化的类型,而它尚未在该语言实现。运行时该语言可以定义一个串行定义为可注入的接口。它还可以定义一个注入方法。该方法定义该语言将为哪个类指定新的串行能力。对相关对象调用该注入方法,就可以完成注入。利用接口注入,可以使 JVM 中的动态类型语言很方便地与 JVM 中其他语言进行整合。

  总 结

  多年来,在 JVM 上运行的语言越来越多。在 JVM 中支持动态类型语言,对于使用动态语言的开发者非常具有吸引力。因为,动态类型让开发者更具灵活性,而且 JVM 具有更好的执行效率。但是,对于动态类型语言,满足方法调用的字节码的要求非常困难。为了应对这一难题,JSR 292 提供了新的字节码 invokedynamic 以及新的基于方法句柄的连接机制。此外,目前还在进行调研在 JSR 292 中引入接口注入,它能够在运行修改类,从而可以实现新的接口。

0
相关文章