JavaMemoryModel: final, pseudo-final and write protected fields

From: Bill Pugh (pugh@cs.umd.edu)
Date: Wed Oct 20 1999 - 16:13:39 EDT


Here are some of the semantics I am thinking about
for when final fields aren't really final. These assume
that, when bad things don't happen, we want to guarantee
seeing the correct value for a final field and that we want
to allow aggressive compiler optimization of final fields
(e.g., assume that you never have to reload a final field).

Some fields declared as final are modified by native methods;
in particular, System.in, System.out and System.err. I don't
think we can make the semantics we want for final fields
apply to fields changed by native methods. On the other
hand, I don't think it is possible to eliminate the fact
that these fields can be changed.

So a proposed solution is to treat these fields as write protected
fields. The only special semantics of a write protected field is that
putstatic/putfield opcodes can only modify them in the class
or instance initializer. But otherwise, they are treated as
a regular field by the semantics.

Of course, this leaves open the question of "Which fields should
be write protected, rather than final"? I think we have only two
choices:
  * Declare that System.in, System.out and System.err are the
    only write protected fields that can exist, and that changing
    any other field from native code is horribly evil.

  * Provide some source level annotation to declare which fields
    are write protected rather than final, and state that it is
    horribly evil to modify a final field from native code.

OK, what about pseudo-final? A field is pseudo-final if it is
read before it is assigned a value, or if a reference to object
is stored into the heap before the field is assigned. The semantics
of being pseudo-final are very bad: basically, anytime you read
a pseudo-final field, you may get back either the default value
or the assigned value (if one has been assigned). Nothing you can
do (synchronization, ...) will fix this. Once a field has become
pseudo-final, it is in an irreparable state.

As an example of why we need pseudo-final fields, consider this
code:
public class A {
  final int x;
  static A I(A a) { return a; }
  A() {
    A a = I(this);
    int i = a.x;
    this.x = 42;
    int j = a.x;
    }
  }
In looking at the constructor for A, it seems that
we should be able to replace the second read of a.x
with a reuse of the previous loaded value; after all,
a.x is final and cannot change.

But of course, in this case, it can.

So those are my suggestions. They are fairly aggressive in terms
of punishing code with pseudo-final fields. I could try to come up
with something that makes more guarantees for pseudo-final fields,
but doing so would likely complicate the formal description of
things, and possibly limit optimization. So do people think my
proposal is acceptable, or that we should make more guarantees.

Note that static compiler analysis can tell you whether a final
field might or definitely will be pseudo-final. This could be used
to produce warnings, and possibly to treat such fields as write
protected fields.

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



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