JEP 334:JVM 常量 API
概述
引入一个 API,以对关键的类文件和运行时工件的名义描述进行建模,特别是可以从常量池加载的常量。
动机
每个 Java 类文件都有一个常量池,其中存储了类中字节码指令的操作数。大体来说,常量池中的条目描述的要么是运行时的产物(例如类和方法),要么是简单的值(例如字符串和整数)。所有这些条目都被称为可加载常量,因为它们可以作为 ldc
指令("load constant",即“加载常量”)的操作数。它们也可能出现在 invokedynamic
指令的引导方法的静态参数列表中。执行 ldc
或 invokedynamic
指令时,可加载常量会被解析为标准 Java 类型(如 Class
、String
或 int
)的“活跃”值。
需要操作 class
文件的程序必须对字节码指令进行建模,进而对可加载常量进行建模。然而,使用标准的 Java 类型来建模可加载常量是不够的。对于描述字符串的可加载常量(CONSTANT_String_info
条目)来说,这种方法可能是可以接受的,因为生成一个“活动的” String
对象非常简单;但对于描述类的可加载常量(CONSTANT_Class_info
条目)来说,这种方法是有问题的,因为生成一个“活动的” Class
对象依赖于类加载的正确性和一致性。遗憾的是,类加载有许多环境依赖和失败模式:所需的类可能不存在,或者请求者无法访问;类加载的结果因上下文而异;加载类会产生副作用;有时类加载可能根本无法完成(例如,当被描述的类尚不存在或因其他原因无法加载时,比如在编译这些类期间,或在 jlink
时间转换期间)。
因此,如果程序能够以纯粹的名义和符号形式处理类、方法以及不太为人所知的构造(如方法句柄和动态计算常量),那么涉及可加载常量的程序将会更加简单:
-
字节码解析和生成库必须以符号形式描述类和方法句柄。如果没有标准机制,它们就不得不采用特定的机制,无论是像 ASM 的
Handle
这样的描述符类型,还是字符串元组(方法拥有者、方法名称、方法描述符),或者是将这些编码为单一字符串的特定(且容易出错)方式。 -
如果操作字节码(例如
LambdaMetafactory
)的invokedynamic
引导程序能够在符号域中工作,而不是使用“实时”类和方法句柄,那么它们会更加简单。 -
编译器和离线转换器(例如
jlink
插件)需要描述无法加载到运行时虚拟机中的类及其成员。类似地,编译器插件(例如注解处理器)也需要以符号形式描述程序元素。
这些库和工具都会受益于有一个单一的、标准的方式来描述可加载常量。
描述
我们定义了一组基于值的符号引用(JVMS 5.1)类型,位于新包 java.lang.invoke.constant
中,能够描述每种可加载常量。符号引用以纯名义的形式描述可加载常量,独立于类加载或访问上下文。某些类可以充当其自身的符号引用(例如,String
);对于可链接常量,我们定义了一组符号引用类型(ClassDesc
、MethodTypeDesc
、MethodHandleDesc
和 DynamicConstantDesc
),它们包含描述这些常量的名义信息。
依赖关系
此 JEP 最初是 JEP 303(LDC 和 INVOKEDYNAMIC 指令的内在实现) 的一个子功能。现在,JEP 303 依赖于此 JEP。