`

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

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

动态类型语言和 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)

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 中引入接口注入,它能够在运行修改类,从而可以实现新的接口。

原文:New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine

  • 大小: 18.6 KB
分享到:
评论

相关推荐

    揭秘Java虚拟机-JVM设计原理与实现

    7. **多线程**:JVM内置对多线程的支持,每个线程有自己的程序计数器、本地方法栈和虚拟机栈,共享堆和方法区。 8. **类文件结构**:深入理解`.class`文件的结构,包括魔数、版本号、常量池、字段表、方法表、属性...

    Java虚拟机:JVM高级特性与最佳实践(第二版)

    - **动态类型**:Java 7引入的 invokedynamic 指令,支持动态语言的调用约定。 7. **异常处理与安全** - **异常处理**:Java的异常处理机制,包括try-catch-finally、throws、throw关键字,以及异常链。 - **...

    实战Java虚拟机:JVM故障诊断与性能优化

    资源名称:实战Java虚拟机:JVM故障诊断与性能优化资源截图: 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。

    深入理解Java虚拟机JVM高级特性与最佳实践1

    在类加载机制方面,作者解释了JVM是如何在运行时动态加载和链接Java类的,包括双亲委派模型、类加载器的种类以及自定义类加载器的设计与实现。理解这些机制对于构建灵活的应用程序架构至关重要,尤其是在处理热部署...

    Java虚拟机:JVM高级特性与最佳实践(第2版)

    7. **最新特性**:第二版可能涵盖了Java新版本中的JVM相关更新,比如Java 8的Lambda表达式和Stream API在JVM上的实现,或者Java 11的模块系统对JVM的影响。 通过阅读这本书,开发者不仅能够掌握JVM的内部运作,还能...

    揭秘Java虚拟机_JVM设计原理与实现.part2

    需要两部分一起解压才可以: 当前为第二部分。 第一部分地址如下: https://download.csdn.net/download/dcxy0/10374640 也可以通过下面的地址找另外第一部分的文件:https://download.csdn.net/user/dcxy0/uploads

    java.net.BindException: Address already in use: JVM_Bind :8088(端口冲突)

    在myeclipse中将html文件改成jsp文件时myeclipse卡住;将之前的任务关掉;再打开时多次部署项目的时候报错

    新手必须学习资料:JVM详解

    * 高度动态性:Java 具有高度的动态性,能够在运行时动态加载类库和对象。 JVM 的原理 JVM 的原理可以分为三部分:类加载器(Class Loader)、执行引擎(Execution Engine)和运行时数据区(Runtime Data Area)。...

    精通Java监控:JVM监控工具的使用与实践

    Java虚拟机(JVM)监控工具是确保Java应用程序性能和稳定性的重要资源。这些工具可以帮助开发者监控和分析JVM的运行时状态,包括内存使用、垃圾回收行为、线程活动等关键指标。本文将详细介绍如何在Java中使用JVM...

    Java运行原理与Java虚拟机.pdf

    6. **动态链接**:JVM支持动态链接,这意味着可以在运行时动态加载类库和资源。这对于构建大型分布式系统非常有用,因为它允许系统在运行过程中动态调整行为。 #### 三、Java虚拟机的安全性 Java虚拟机通过多种...

    实战Java虚拟机 JVM故障诊断与性能优化 葛一鸣

    首先,JVM是Java语言的核心组成部分,它为Java应用程序提供了运行环境。JVM的主要任务是解析字节码,并将其转化为机器语言,使得Java程序能够在不同的操作系统上无缝运行,实现了“一次编写,到处运行”的目标。理解...

    JVM内幕:java虚拟机详解

    ### JVM内幕:java虚拟机详解 #### 一、概述 Java虚拟机(JVM)是运行Java应用程序的核心组件,它提供了一个可移植、安全且高性能的环境。本文将深入探讨JVM的内部架构及其各个组成部分的功能。 #### 二、Java虚拟机...

    Tjcug#JavaBasciLearing#Java基础:由JVM内存模型详解线程安全1

    1.前言最近在研究JVM内存模型和Java基础知识。主要讲的是线程共享变量与线程私有变量以及如何写出线程安全的代码。这里列出一条规则,“类中的成员变量,也叫实例

    解决jvmjava.net.BindException: Address already in use: JVM_Bind异常

    【Java中的`java.net.BindException: Address already in use: JVM_Bind`异常】 在Java编程中,当你尝试启动一个服务器端应用,如Tomcat,或者任何需要监听特定端口的服务时,可能会遇到`java.net.BindException: ...

    java虚拟机JVM详解ppt

    - **与平台无关性**:Java语言的一个关键特性是其跨平台能力。JVM通过屏蔽不同操作系统之间的差异,使得Java程序无需重新编译即可在不同的平台上运行。 - **自动内存管理**:JVM提供了一套自动化的内存管理机制,...

    深入理解Java虚拟机:JVM高级特性与最佳实践(第2版) pdf下载-附件资源

    深入理解Java虚拟机:JVM高级特性与最佳实践(第2版) pdf下载-附件资源

    Java基础知识学习:包括JVM虚拟机、对象模型等Java基础知识代码案例.zip

    Java基础知识学习:包括JVM虚拟机、对象模型等Java基础知识代码案例Java基础知识学习:包括JVM虚拟机、对象模型等Java基础知识代码案例Java基础知识学习:包括JVM虚拟机、对象模型等Java基础知识代码案例Java基础...

    java JVM 详解

    * 动态加载:Java 语言支持动态加载类库,实现了动态加载和卸载类库的功能。 * 解释执行:Java 语言的执行方式是解释执行,先将 Java 源代码编译成字节码,然后由 JVM 解释执行字节码。 Java 的开发流程 Java 的...

    实战JAVA虚拟机 JVM故障诊断与性能优化.pdf

    实战JAVA虚拟机 JVM故障诊断与性能优化.pdf (无书签.低分放送) 实战JAVA虚拟机 JVM故障诊断与性能优化.pdf (无书签.低分放送) 实战JAVA虚拟机 JVM故障诊断与性能优化.pdf (无书签.低分放送)

    《深入理解Java虚拟机:JVM高级特性与最佳实践(最新第二版》-附件资源

    《深入理解Java虚拟机:JVM高级特性与最佳实践(最新第二版》-附件资源

Global site tag (gtag.js) - Google Analytics