1. 前言
自 Groovy 2.0 以来,添加了对 JVM invokedynamic 指令的支持。此指令在 Java 7 及更高版本中受支持,是 JVM 中的一种新的字节码指令,它允许更轻松地实现动态语言。此指令也将由 JVM 内部使用,用于 Java 8 中的 lambda 支持。
这意味着,与 API、AST 变换或语法糖不同,此功能对开发人员或最终用户来说是不可见的。它只是一种编译时和运行时功能。这意味着对于用 Groovy 编写的两个程序,您可以选择使用或不使用 invokedynamic 支持进行编译。无论您选择什么,都会有优缺点。
-
只要您运行 JDK 1.7+,就可以在同一个项目中混合使用使用和不使用 invokedynamic 编译的类。
-
根据 JVM(甚至 JVM 的不同次要版本),您可以针对使用 invokedynamic 支持激活的动态 Groovy 接近 Java 的性能。
2. 发行版
2.1. 两个 jar 包
Groovy 发行版包含两个 jar 包
-
groovy-x.y.z.jar:包含使用调用站点缓存编译的 Groovy 源代码
-
groovy-x-y-z-indy.jar:包含使用 invokedynamic 指令编译的 Groovy 源代码
由于 Groovy 核心和 Groovy 模块有时是用 Groovy 编写的,因此我们目前别无选择,只能发布两个不同的 Groovy 版本。这意味着如果您选择“正常”jar 包,Groovy 本身的 Groovy 类将使用调用站点缓存(1.6+)编译,而如果您使用“indy”jar 包,Groovy 本身的 Groovy 类将使用 invokedynamic 编译。
这两个 jar 包都包含一个功能完备的 Groovy 实现,能够使用 invokedynamic 或调用站点缓存编译用户提供的 Groovy 源代码。这些 jar 包集是互斥的(不要将它们都放在类路径中),它们之间的主要区别在于用于编译组成 Groovy 本身的 Groovy 源文件的方式。
2.2. 命令行和 indy
如果您下载了发行版并使用命令行,则始终是“正常”版本的 Groovy 被添加到类路径中。这意味着无论您使用什么命令(groovy
、groovyc
、groovysh
或 groovyConsole
),invokedynamic 支持都不会默认可用。要使用为其 Groovy 源代码编译了 invokedynamic 的 Groovy 发行版,您必须手动切换 jar 包。发行版使用 lib 目录中的 jar 包,而 indy jar 包在 indy 目录中可用。您需要做三件事
-
删除或重命名 lib 目录中的 groovy-*.jar 文件
-
用 indy 目录中的 indy 版本替换它们
-
从 jar 包名称中删除 -indy 分类器
这是一个 bash 脚本,可以一次性完成所有操作
$ for f in `ls lib/groovy*.jar | cut -d/ -f2`;do k=`basename $f .jar`; mv lib/$k.jar lib/$k.jar.old; cp indy/$k-indy.jar lib/$k.jar ; done
3. 从命令行运行 Groovy 脚本
从命令行运行脚本的通常方法是 groovy foo.groovy
,其中 foo.groovy
是以源代码形式存在的 Groovy 程序。要为此使用 indy,您必须使用 indy 编译标志,groovy --indy foo.groovy
。
4. 编译标志
独立于您使用的 jar 包版本(以及交换完 jar 包后,如前所述),invokedynamic 支持需要一个特定的编译标志(indy)。如果您想使用 invokedynamic 支持编译您的类,则必须在编译时设置此标志。以下表格显示了根据您使用的 jar 包和编译标志,用户编译的类和 Groovy 核心类会发生什么情况
indy 标志 | 关闭 | 开启 |
---|---|---|
正常 jar 包 |
调用站点缓存 |
invokedynamic |
indy jar 包 |
调用站点缓存 |
invokedynamic |
indy 标志 | 关闭 | 开启 |
---|---|---|
正常 jar 包 |
调用站点缓存 |
调用站点缓存 |
indy jar 包 |
invokedynamic |
invokedynamic |
因此,即使您使用 indy jar 包,如果您在编译时不使用 invokedynamic 标志,那么编译后的类将使用“旧”格式。