JLS 是否需要内联最终字符串常量? [英] Does the JLS require inlining of final String constants?
问题描述
我在操作一些字节码时遇到了一个问题,其中某个 final
String
常量没有被 java 编译器 (Java 8) 内联,请参见下面的示例:
I ran into an issue while manipulating some bytecode, where a certain final
String
constant was not inlined by the java compiler (Java 8), see the example below:
public class MyTest
{
private static final String ENABLED = "Y";
private static final String DISABLED = "N";
private static boolean isEnabled(String key) {
return key.equals("A");
}
private static String getString(String key, String value) {
return key + value;
}
public static void main(String[] args) throws Exception {
String flag = getString("F", isEnabled("A") ? ENABLED : DISABLED);
System.out.println(flag);
String flag2 = getString("F", isEnabled("A") ? ENABLED : DISABLED);
System.out.println(flag2);
}
}
使用 javac (1.8.0_101) 生成的字节码
Resulting bytecode with javac (1.8.0_101)
public static void main(java.lang.String[]) throws java.lang.Exception;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #8 // String F
2: ldc #2 // String A
4: invokestatic #9 // Method isEnabled:(Ljava/lang/String;)Z
7: ifeq 16
10: getstatic #10 // Field ENABLED:Ljava/lang/String;
13: goto 19
16: getstatic #11 // Field DISABLED:Ljava/lang/String;
19: invokestatic #12 // Method getString:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
22: astore_1
23: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_1
27: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: ldc #8 // String F
32: ldc #2 // String A
34: invokestatic #9 // Method isEnabled:(Ljava/lang/String;)Z
37: ifeq 46
40: getstatic #10 // Field ENABLED:Ljava/lang/String;
43: goto 49
46: getstatic #11 // Field DISABLED:Ljava/lang/String;
49: invokestatic #12 // Method getString:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
52: astore_2
53: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
56: aload_2
57: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
60: return
您可以看到第二次访问字段 ENABLED
和 DISABLED
时,编译器没有内联它们的值(使用 ldc
),而是使用 getstatic
直接访问该字段.使用其他编译器(Java 7、Eclipse)对其进行测试并没有触发相同的行为,并且常量始终是内联的.
You can see that the second time the fields ENABLED
and DISABLED
are being accessed, the compiler did not inline their values (using ldc
), but instead used getstatic
to access the field directly. Testing it with other compilers (Java 7, Eclipse) did not trigger the same behavior and the constants were always inlined.
这是否可以被视为编译器错误,或者是否允许根据 JLS 始终不内联字符串常量?
Can this be considered a compiler bug, or is it allowed to not inline string constants all the time according to the JLS?
推荐答案
是的,规范要求内联"行为:
Yes, the "inlining" behavior is mandated by the specification:
…
对作为常量变量的字段的引用(第 4.12.4 节)必须在编译时解析为常量变量的初始值设定项所表示的值
V
.
如果这样的字段是static
,那么二进制文件中的代码中不应出现对该字段的引用,包括声明该字段的类或接口.这样的字段必须始终显示已初始化(第 12.4.2 节);不得观察字段的默认初始值(如果不同于 V
).
If such a field is static
, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field. Such a field must always appear to have been initialized (§12.4.2); the default initial value for the field (if different than V
) must never be observed.
如果这样的字段是非静态
,那么在二进制文件的代码中不应该存在对该字段的引用,除了在包含该字段的类中.(它将是一个类而不是接口,因为接口只有 static
字段.)该类应该有代码在实例创建期间将字段的值设置为 V
(第 12.5 节).
If such a field is non-static
, then no reference to the field should be present in the code in a binary file, except in the class containing the field. (It will be a class rather than an interface, since an interface has only static
fields.) The class should have code to set the field's value to V
during instance creation (§12.5).
注意,这如何准确地解决您的情况:如果这样的字段是 static
,那么二进制文件的代码中不应存在对该字段的引用,包括类或声明字段的接口".
Note, how this precisely addresses you scenario: "If such a field is static
, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field".
换句话说,如果您遇到不遵守此规定的编译器,您就发现了一个编译器错误.
In other words, if you encounter a compiler not adhering to this, you found a compiler bug.
作为附录,查找此信息的起点是:
As an addendum, the starting point for finding this information was:
…
常量变量是原始类型或类型String
的final
变量,用常量表达式(§15.28).变量是否为常量变量可能对类初始化有影响 (§12.4.1),二进制兼容性(§13.1, §13.4.9)和明确的分配(§16(明确分配)).
A constant variable is a final
variable of primitive type or type String
that is initialized with a constant expression (§15.28). Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1, §13.4.9), and definite assignment (§16 (Definite Assignment)).
这篇关于JLS 是否需要内联最终字符串常量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!