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