Java 更改JIT编译的最终值

Java 更改JIT编译的最终值,java,reflection,Java,Reflection,我注意到一件非常奇怪的事情,在通过反射改变了最终字段之后,返回该字段的方法一直在给出旧值。我想这可能是因为JIT编译器 以下是示例程序: public class Main { private static final Main m = new Main(); public static Main getM() { return m; } public static void main(String args[]) throws Exception { Main m = get

我注意到一件非常奇怪的事情,在通过反射改变了最终字段之后,返回该字段的方法一直在给出旧值。我想这可能是因为JIT编译器

以下是示例程序:

public class Main
{
private static final Main m = new Main();
public static Main getM()
{
    return m;
}

public static void main(String args[]) throws Exception
{
    Main m = getM();
    int x = 0;
    for(int i = 0;i<10000000;i++)
    {
        if(getM().equals(m))
            x ++;
    }
    Field f = Main.class.getDeclaredField("m");
    f.setAccessible(true);
    removeFinal(f);
    Main main1 = new Main();
    f.set(null, main1);
    Main main2 = (Main) f.get(null);
    Main main3 = getM();
    System.out.println(main1.toString());
    System.out.println(main2.toString());
    System.out.println(main3.toString());
}

private static void removeFinal(Field field) throws NoSuchFieldException, IllegalAccessException
{
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
}
我想知道,如何让getM()返回更新的值

我想知道,如何让getM()返回更新的值

期末考试,你不能。返回“旧”值是一种合法行为,如下所示:

即便如此,也存在一些复杂问题。如果最后一个字段是 初始化为字段中的常量表达式(§15.28) 声明时,可能无法观察到对最终字段的更改,因为 最后一个字段的使用在编译时被替换为值 常数表达式的一种形式

另一个问题是该规范允许攻击性攻击 最终字段的优化。在螺纹内,允许 通过对最终字段的修改对最终字段的读取进行重新排序 在构造函数中不发生的字段

请参阅该章中包含的指导性示例


克服这一规定的尝试将不得不包括在堆栈中搞乱优化器,而且充其量是脆弱的。如果您选择修改字段,那么根据定义,这些字段不应该是最终字段。如果出于性能原因需要这样做(真的吗?),那么JSR292提供了进行构造的机制。

如果真的想更改该值,可以包装该类并重写getter


也许您听说过“委托”/“代理”模式。

关键字
final
允许编译器内联。为什么需要这样做?有没有办法强制JIT重新编译该方法?javac编译器,而不是JIT。removeFinal方法中有什么?这不是由javac引起的,您可以通过使循环仅迭代一次(而不是10000000次)来发现,然后所有结果都将是相同的。当我的应用程序中出现问题时,有一些方法可以在不重新启动应用程序的情况下临时修复/隐藏问题,方法是加载具有静态方法访问权限的新的小jar。有时我需要更改最终字段以修复某些问题。通常情况下,反射在这种情况下可以很好地工作,但有时由于问题本身的原因,反射并不起作用。所以我想没有办法克服这一点。
Main@1be6f5c3
Main@1be6f5c3
Main@6b884d57