-->

Change final value compiled by JIT

2020-08-13 07:37发布

问题:

I noticed a very strange thing that after changing final field via Reflection, method returning that field is all the time giving old value. I suppose this might be because of JIT compiler.

Here is sample program:

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

Result is:

Main@1be6f5c3
Main@1be6f5c3
Main@6b884d57

I am wondering, how can i make getM() return updated value?

回答1:

I am wondering, how can i make getM() return updated value?

With finals, you can't. Returning "old" value is a legitimate behavior, as JLS 17.5.3:

Even then, there are a number of complications. If a final field is initialized to a constant expression (§15.28) in the field declaration, changes to the final field may not be observed, since uses of that final field are replaced at compile time with the value of the constant expression.

Another problem is that the specification allows aggressive optimization of final fields. Within a thread, it is permissible to reorder reads of a final field with those modifications of a final field that do not take place in the constructor.

See the instructive example that is included in that chapter.

Attempts to overcome this provision would have to include messing with optimizers down the stack, and are fragile at best. If you are choosing to modify fields, then, by definition, those fields should not be final. If you want this for performance reasons (do you, really?), then JSR 292 provides the mechanics to do "almost final" constructions.



回答2:

If you really want to change that value, You could wrap that class and override getter.

Maybe You've heard something about "Delegate" / "Proxy" pattern.