更改JIT编译的最终值

use*_*344 10 java reflection

我注意到一个非常奇怪的事情,通过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 = 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);
}
}
Run Code Online (Sandbox Code Playgroud)

结果是:

Main@1be6f5c3
Main@1be6f5c3
Main@6b884d57
Run Code Online (Sandbox Code Playgroud)

我想知道,我如何使getM()返回更新值?

Ale*_*lev 12

我想知道,我如何使getM()返回更新值?

有了决赛,你不能.返回"旧"值是合法行为,如JLS 17.5.3:

即使这样,也有许多并发症.如果在字段声明中将final字段初始化为常量表达式(第15.28节),则可能无法观察到对final字段的更改,因为在编译时将该final字段的使用替换为常量表达式的值.

另一个问题是规范允许对最终字段进行积极优化.在一个线程中,允许使用构造函数中不发生的最终字段的那些修改来重新排序最终字段的读取.

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

尝试克服这一规定将不得不包括在堆栈中弄乱优化器,并且充其量是脆弱的.如果您选择修改字段,那么根据定义,这些字段不应该是最终字段.如果你出于性能原因想要这个(你真的吗?),那么JSR 292提供了做"几乎最终"结构的机制.