JLS是否要求对最终String常量进行内联? [英] Does the JLS require inlining of final String constants?

查看:60
本文介绍了JLS是否要求对最终String常量进行内联?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在处理某些字节码时遇到了一个问题,其中某个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

您可以看到,第二次访问字段ENABLEDDISABLED时,编译器没有内联它们的值(使用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:

13.1.二进制的形式

...

13.1. The Form of a Binary

  1. 对作为常量变量(第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,则除了包含该字段的类之外,二进制文件的代码中不应存在对该字段的引用. (这将是一个类,而不是一个接口,因为一个接口只有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:

4.12.4.最终变量

...

4.12.4. final Variables

常量变量是原始类型或类型为Stringfinal变量,并使用常量表达式(

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是否要求对最终String常量进行内联?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆