RE: JavaMemoryModel: Final fields and deserialization

From: Evan Ireland (eireland@sybase.com)
Date: Tue May 11 2004 - 19:47:30 EDT


Bill,

I'm very pleased with this outcome.

As an implementor of custom RMI-IIOP serialization, I have been through
hell and back with the non-portable alternate mechanisms for setting
final fields.

In making this change, we have now permitted write-once, run-anywhere
for J2EE containers.

Thanks to everyone who helped bring this about (including Doug Lea).

> -----Original Message-----
> From: owner-javamemorymodel@cs.umd.edu
> [mailto:owner-javamemorymodel@cs.umd.edu]On Behalf Of Bill Pugh
> Sent: Wednesday, 12 May 2004 3:21 a.m.
> To: javamemorymodel-cs.umd.edu; JSR133
> Subject: JavaMemoryModel: Final fields and deserialization
>
>
>
> Previously, we had made some fields final, such as fields of the String
> and Integer classes,
> so that these classes would be truly immutable, even if references to
> them were passed between
> threads via data races.
>
> This turns out to have caused some critical applications to fail. These
> applications
> were using their own deserialization mechanisms, and they were changing
> fields via
> reflection. With the additional fields being final, the applications no
> longer worked.
>
> These applications in are the category of applications that must work
> in order for
> Sun to consider the JVM acceptable.
>
> So the solution that has been decided on is that if you call
> setAccessible on
> a java.lang.reflect.Field object, this will allow you not only to
> change private
> fields of other classes, but also to change final fields. Note that
> special security
> permissions are required to call setAccessible, and if you have these
> permissions you
> can also remove or change the SecurityManager, so there isn't a big new
> security hole
> created by this.
>
> At this point, it is about 99.99% certain that this is the solution
> that will be used,
> although there is a slight chance it will make it into the release
> candidate rather
> than beta2.
>
> This is probably for the best. When people start using final fields
> more widely,
> it was going to become impossible for people to writing deserialization
> code without
> using special magic like Unsafe.
>
> We are making some slight adjustments to the details of changing final
> fields in
> the formal semantics, so that normally written deserialization code
> will (almost always)
> just work. The semantics are only designed to handle the case where the
> final fields
> are modified via reflection before the field is read. There is one case
> that is
> problematic:
>
> class A {
> final int x;
> A() {
> x = 1;
> }
> int f() {
> return d(this,this);
> }
> int d(A a1, A a2) {
> int i = a1.x;
> g(a1);
> int j = a2.x;
> return j - i;
> }
> static void g(A a) {
> // uses reflection to change a.x to 2
> }
> }
>
> We want to allow compilers to reorder reads of final fields across
> unknown method calls.
> Thus, the read of a1.x can be moved to after the call to g(a1), and the
> read of a2.x can
> be moved above the call to g(a1). As a result, f() can return either
> -1, 0 or 1.
>
> To guarantee that this cannot happen, we allow a block of code to be
> performed in a
> final field safe context (probably by giving a runnable to some static
> executor method,
> or by using an annotation on a method).
>
> The semantics of this are that if an object is initialized and then the
> final field
> modified within the final field safe context, reads of the final field
> that occur
> after the final field safe context are guaranteed to see the correct
> value. More
> generally, reads of final fields may not be moved into or out of a
> final field safe context,
> although they may be moved across it (thus, if g used a final field
> safe context above,
> the anomalous behavior could still occur).
>
> We can also use a final field safe context in something such as an
> executor or thread pool,
> to ensure that if one runnable uses an incorrectly published reference
> to an object,
> a subsequent runnable can use a correctly published reference and be
> guaranteed to
> see the correct values for the final fields of the object.
>
> Now, although the spec will describe it, the 1.5 Java API won't provide
> any way to
> obtain a final field safe context. I believe we will get this
> introduced in the 1.5.1
> API. Aggressive optimization of final fields probably won't come until
> 1.5.1 anyway,
> so this should be OK.
>
> Note also that changing a final field initialized to a compile time
> constant
> is highly questionable, since the value is substituted for uses of the
> field by
> the compiler. It is allowed, but any use is allowed to see the compile
> time constant
> value.
>
> So the decision to allow reflection to change final fields, assuming
> the appropriate
> security permissions, is pretty much a done deal. There really wasn't
> much time to
> debate it, sorry about that.
>
> However, we are open to feedback on the formal semantics of doing so.
>
> Previously we described a method realloc method that needed to be used
> when
> modifying final fields. After thinking about it, we decided that this
> was unlikely
> to be something that could be easily retrofitted to most
> deserialization techniques.
> Instead, we are tweak the semantics so that deserialization code
> written to handle
> non-final fields will also work for final fields, with a requirement
> for using this new "final field safe context" to absolutely guarantee
> that the
> compiler won't reorder a read of a final field and a reflective change
> to a final
> field within a thread.
>
>
>
> Bill
>
> -------------------------------
> JavaMemoryModel mailing list -
http://www.cs.umd.edu/~pugh/java/memoryModel

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



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