JLS 是否需要内联最终字符串常量? [英] Does the JLS require inlining of final String constants?

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

问题描述

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

  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 字段.)该类应该有代码在实例创建期间将字段的值设置为 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:

常量变量是原始类型或类型Stringfinal变量,用常量表达式(§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屋!

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