是否可以禁用javac对静态最终变量的内联?

2022-08-31 17:36:44

Java 静态编译器 (javac) 内联一些静态最终变量,并将值直接引入常量池。请考虑以下示例。类 A 定义了一些常量(公共静态最终变量):

public class A {
    public static final int INT_VALUE = 1000;
    public static final String STRING_VALUE = "foo";
}

B 类使用以下常量:

public class B {
    public static void main(String[] args) {
        int i = A.INT_VALUE;
        System.out.println(i);
        String s = A.STRING_VALUE;
        System.out.println(s);
    }
}

编译类 B 时,javac 从类 A 获取这些常量的值,并将这些值内联到 B.class 中。因此,在编译时,B 必须从字节码中擦除类 A 的依赖关系。这是一个相当奇特的行为,因为在编译时您正在烘焙这些常量的值。您可能会认为这是 JIT 编译器在运行时可以执行的最简单的事情之一。

有没有办法或任何隐藏的编译器选项可以让你禁用javac的这种内联行为?对于背景,我们正在研究出于依赖关系目的进行字节码分析,这是字节码分析无法检测编译时依赖关系的少数情况之一。谢谢!

编辑:这是一个令人烦恼的问题,因为通常我们无法控制所有源代码(例如定义常量的第三方库)。我们有兴趣从使用常量的角度检测这些依赖关系。由于引用是从使用常量的代码中删除的,因此除了执行源代码分析之外,没有简单的方法来检测它们。


答案 1

Java Puzzlers(Joshua Bloch)的第93项说,你可以通过防止最终值被视为常量来解决这个问题。例如:

public class A {
  public static final int INT_VALUE = Integer.valueOf(1000).intValue();
  public static final String STRING_VALUE = "foo".toString();
}

当然,如果您无权访问定义常量的代码,则这些都无关紧要。


答案 2

我不这么认为。最简单的解决方法是将这些内容公开为属性而不是字段:

public class A {
    private static final int INT_VALUE = 1000;
    private static final String STRING_VALUE = "foo";

    public static int getIntValue() {
        return INT_VALUE;
    }
    public static String getStringValue() {
        return STRING_VALUE;
    }
}

不要忘记,在某些情况下,内联对于值的使用至关重要 - 例如,如果要在开关块中用作大小写,则必须将其指定为常量值。INT_VALUE


推荐