JavaMemoryModel: Final Means Different Things To VM and Java Language. Does That Impact JITs?

From: Allan Kielstra (kielstra@ca.ibm.com)
Date: Tue Sep 30 2003 - 15:12:18 EDT


My colleague Andrew Johnson has observed that:
   * the Java Language Specification ensures that final fields are only set
once, in the constructor.
   * The byte code verifier just ensures that final fields are only set by
methods in the defining class.

Depending on how pedantic one wants to be, this may limit the flexibility
of the Just-in-time compiler in making optimizations with final fields, as
there is nothing prohibiting such fields changing. Given a program such
as:
/**
 * Test final fields
 */
public class finalt1 {
    /** private final field */
    private final int MyFieldi;
    /** private field - modify to be final using debugger */
    private int MyFieldj;
    public int get() {
        return MyFieldi;
    }
    public int get1(int y) {
        if (y > 0) return get1(y-1);
        return MyFieldj;
    }
    /**
     * Sets MyFieldj to y
     */
    public int set1(int y, int y2) {
        if (y2 > 0) return set1(y, y2-1);
        return MyFieldj = y;
    }
    /**
     * constructs object and calls set1 to set MyFieldj
     */
    public finalt1(int z) {
        if (z == Integer.MIN_VALUE) {
            return;
        }
        MyFieldi = z;
        set1(z, 0);
        MyFieldj = 0;
    }
    public static void main(String[] args) {
        finalt1 q = new finalt1(args.length+Integer.MIN_VALUE);
        System.out.println("Object "+q+" MyFieldi "+q.get()+" MyFieldj
"+q.get1(0));
        q.set1(2, 0);
        System.out.println("Object "+q+" MyFieldi "+q.get()+" MyFieldj
"+q.get1(0));
        int ss = 0;
        for (int i = 0; i < 3000; ++i) {
            ss += q.r(i);
        }
        System.out.println(""+q+" MyFieldi "+q.get()+" MyFieldj
"+q.get1(0)+" "+ss);
    }
    public int r(int t) {
        int y = 0;
        for (int i = 0; i < 100; ++i) {
           set1(t, 0);
           y += MyFieldj;
        }
        return y;
    }
}

If one were to use a binary editor to modify the resulting class file such
that MyFieldj appears to be final, we get a case where the compiler assumes
that the
   "y += MyFieldj"
in the loop in method "r" can be statically computed (i.e., y +=
100*MyFieldj) despite the fact that the method "set1" changes the value of
the field.

My feeling is that if you lie to the VM (by using a class file editor or
other chicanery) you get what you get. That is, a JIT (or anything else)
should be allowed to assume that you're telling the truth. Are there other
opinions?

Andrew has also noted that the jikes compiler (an open source Java compiler
available here:
http://oss.software.ibm.com/developerworks/opensource/jikes/) handles
field initializers by putting all the code into a method called:
private void this()
which is then invoked at the start of each constructor.

Unfortunately, one can re-run the "this" method at arbitrary program points
using reflection:

import java.lang.reflect.*
class C {
   static int StaticGlobal = 0;

   private final int MyFinalField = ++StaticGlobal; // this statement
will be placed in a method called "this"

   public void set() {
        Class z = getClass();
        try {
            /* invoke the private void this() method from Jikes */
            /* Cannot do it directly from java code */
            Method m = z.getDeclaredMethod("this", new Class[]{});
            m.invoke(this, new Object[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 }

Given an object "c" of class "C", the invocation "c.set()" will cause
"MyFinalField" to assume a new value. This is trickier because the cheat
can be performed with Java source code (instead of a class file editor).
In a slightly more perfect world, there would be a mechanism for a compiler
to encode rules in the class file which the VM would use for prohibiting
some operations that use reflection. In the meantime, I believe that this
is a cheat and a JIT or VM is allowed to assume that it doesn't happen.
Again, I am curious about other opinions. Are there any?

Allan Kielstra
IBM Canada Lab
Phone: +1 (905) 413-3558 T/L 969-3558
kielstra@ca.ibm.com

-------------------------------
JavaMemoryModel mailing list - http://www.cs.umd.edu/~pugh/java/memoryModel



This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:00:51 EDT