【发布时间】:2017-01-16 02:17:59
【问题描述】:
我在处理一些字节码时遇到了一个问题,其中某个 final String 常量没有被 Java 编译器 (Java 8) 内联,请参见下面的示例:
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) 生成的字节码
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)对其进行测试并没有触发相同的行为,并且常量始终是内联的。
这可以被认为是一个编译器错误,还是根据 JLS,它是否允许不一直内联字符串常量?
【问题讨论】:
-
定义“内联”。 JLS 需要pooling。你说的是这个吗?
-
内联意味着用直接将常量值压入堆栈的指令替换getstatic指令,例如ldc 在字符串的情况下,或 iconst_X/bipush/sipush/... 在整数基元的情况下,...
-
@piet.t:我删除了字节码标签,因为语言规范确实不处理字节码。但是,行为的定义没有歧义,可以回答问题。
-
很好看。这确实是一个错误,它已通过对 JDK-8066871 的修复得到修复,该修复程序集成在 JDK 9 中并向后移植到 JDK 1.8.0_102——即 1.8.0_101 加上一个补丁集。请注意,JDK-8066871 有不同的症状,但相同的修复程序可以纠正该错误以及此处描述的错误。
-
我刚刚提交了一份错误报告 (JI-9043578),因为我没有在错误数据库中找到相关条目。我想它可以关闭。
标签: java java-8 language-lawyer javac jls