is it possible to disable javac's inlining of

2019-01-05 01:54发布

The Java static compiler (javac) inlines some static final variables and brings the values directly to the constant pool. Consider the following example. Class A defines some constants (public static final variables):

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

Class B uses these constants:

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);
    }
}

When you compile class B, javac gets the values of these constants from class A and inlines these values in B.class. As a result, the dependency B had to class A at the compile time is erased from the bytecode. This is a rather peculiar behavior because you are baking in the values of these constants at the time of compilation. And you would think that this is one of the easiest things that the JIT compiler can do at runtime.

Is there any way or any hidden compiler option that lets you disable this inlining behavior of javac? For the background, we're looking into doing bytecode analysis for dependency purposes, and it is one of the few cases where bytecode analysis fails to detect compile-time dependencies. Thanks!

Edit: this is a vexing issue because normally we don't control all the source (e.g. third-party libraries that define constants). We're interested in detecting these dependencies from the perspective of using the constants. Since the reference is erased from the code that uses the constants, there is no easy way to detect them, short of doing source code analysis.

9条回答
萌系小妹纸
2楼-- · 2019-01-05 02:31

I feel java tightly relies on dynamic compilation and it doesn't do any fancy compilation logic as like C++.

you can try out some options with JIT compilers which does run-time optimization which may have some options to disable/enable this.

in default javac you may not get that option. you have to use 1. some type of dependency graph like extends or implements 2. using method based linking.

-s

查看更多
爷的心禁止访问
3楼-- · 2019-01-05 02:33

I think this is a serious bug. Java is not C/C++. There is a principle(or not) "Compile once, run everywhere".

In this case, when Class A is changed. Any Classes referencing A.CONST_VALUE must be re-compiled and they hardly know whether Class A is changed.

查看更多
迷人小祖宗
4楼-- · 2019-01-05 02:35

I don't believe so. The simplest workaround would be to expose these as properties rather than fields:

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;
    }
}

Don't forget that in certain cases the inlining is essential to the use of the value - for example, if you were to use INT_VALUE as a case in a switch block, that has to be specified as a constant value.

查看更多
走好不送
5楼-- · 2019-01-05 02:40

I recently came across a similar issue, and, as it was said above, such inlining can be worked around using non-compile-time expressions, say:

public final class A {

    public static final int INT_VALUE = constOf(1000);
    public static final String STRING_VALUE = constOf("foo");

}

where the constOf method family is merely:

// @formatter:off
public static boolean constOf(final boolean value) { return value; }
public static byte constOf(final byte value) { return value; }
public static short constOf(final short value) { return value; }
public static int constOf(final int value) { return value; }
public static long constOf(final long value) { return value; }
public static float constOf(final float value) { return value; }
public static double constOf(final double value) { return value; }
public static char constOf(final char value) { return value; }
public static <T> T constOf(final T value) { return value; }
// @formatter:on

This is a bit shorter than other suggestions like Integer.valueOf(1000).intValue() or null!=null?0: 1000

查看更多
甜甜的少女心
6楼-- · 2019-01-05 02:42

jmake is an open-source project that claims to do the whole job of keeping track of dependencies between Java files and incrementally compiling the minimum set of files required. It claims to correctly handle changes to static final constants, albeit by sometimes requiring the whole project to be recompiled. It even handles changes at a finer granularity than classfiles; if (for example) the signature of a method C.m() changes, then it only recompiles the classes that actually depend on m() rather than all classes that use C.

DISCLAIMER: I have no experience using jmake.

查看更多
Luminary・发光体
7楼-- · 2019-01-05 02:43

Item 93 of Java Puzzlers (Joshua Bloch) says that you can work round this by preventing the final value from being considered a constant. For example:

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

Of course none of this is relevant if you don't have access to the code that defines the constants.

查看更多
登录 后发表回答