RE: JavaMemoryModel: Another Java threading issue (finalization)

From: Jerry Schwarz (
Date: Tue Apr 01 2003 - 13:32:11 EST

At 08:16 AM 4/1/2003, David Holmes wrote:
> > There is absolutely no guarantee that while an object's
> > method is running the object is reachable, or that
> > reference to it is on the call stack.
>Okay - now I understand the problem.
>The specification for the different invocation bytecodes explicitly
>require that objectref be on the stack. So every invocation logically
>starts with the 'this' pointer available in local variable zero and
>the object is reachable.
>The JLS Section 12.6.1 states "A reachable object is any object that
>can be accessed in any potential continuing computation from any live

The next sentence is that "optimizing transformations of a program can be
designed that reduce the number of objects that are reachable to be less
than those which would naively be considered reachable."

There is no definition of what an allowable "optimizing transformation"
might be, but clearly the intent is that it might change when finalizers
are called. It isn't at all clear whether it needs to determine before it
makes such a transformation whether an early call on the finalizer might
have an effect on the result of the program. However, I beleive the usual
interpretation is that it need not take such an effect into account.

Since there are no limits on how smart an optimizer can be, I take this to
mean there are no limits on how early a finalizer can be run. To give a
concrete example, consider the class.

     class S {
        private String msg = "Say what you want"
        public setMsg(String msg) { this.msg = msg; }
        public f() { System.out.println(this.msg); }
        public void finalize() { System.out.println("finalizer called"); }
        public static void main(String[] argv) { S s = new S() ; s.f(); }

Since the compiler can see immediately that s.setMsg is never called it can
inline s.f(), and eliminate all accesses to s Thus the finalizer can be
called immediately after s is constructed and the output could be the
undesired result

       finalizer called
       Say what you want

But the above program has a race because the finalizer runs in a separate
thread, so before addressing the ordering issue we should cure that problem
by synchronizing both f and the finalizer.

     class S {
        private String msg = "Say what you want"
        public setMsg(String msg) { this.msg = msg; }
        synchronized public f() { System.out.println(this.msg); }
        synchronized public void finalize() { System.out.println("finalizer
called"); }
        public static void main(String[] argv) { S s = new S() ; s.f(); }

Now it appears that the lock/unlock of the call of s.f() must be an
"access" so we have prevented not only the race, but the early call of the
finalizer and the undesired output has been disallowed.

I do not believe that is the case for several reasons (any one of which is
sufficient to allow the undesired output).

A. My reading of the standard is that the lock is not required to be part
of s. Even on a system where the lock is normally a part of the object, the
optimizer is allowed to choose a different strategy for some special cases.

B. Because the optimizer doesn't need to take into account the existence of
finalizers when it determines what optimizations are possible, it can see
immediately that (except for the finalizer) no other thread could possibly
lock s, and so it can eliminate the (inlined) lock and unlock. To say that
it can't means that it somehow needs to take into account what finalizers
do when it optimizes.

C. The optimizer can see that it doesn't really have to use the lock in s.
It can achieve the same synchronization affect by using the lock associated
with any object. In this case there is a perfectly good object lying
around, namely System.out that it could synchronize on. It needs to check
that there won't be any deadlocks created by this, but in this simple case
that conclusion is fairly obvious. In a way this is a variant of (A) but
it is a slightly more plausible transformation for a system which normally
puts the locks into the objects.

In short, I do not believe that unless we limit what constitutes an
allowable optimization beyond its normal meaning (doesn't change the effect
of the program) that we can ever guarantee that the output of the program
is not

       finalizer called
       Say what you want

>The system might determine that the 'this' pointer is dead and remove
>that reference allowing GC to finalize and reclaim the object while
>the method is executing. However, if the local reference was dead then
>there can't be any use of that object by the thread that is still
>executing it's method - and so it *seems* safe, even if a little
>On the other hand, if the local reference was not dead then the
>optimisation is invalid and its a VM bug. If a VM "optimises" things
>by placing the 'this' pointer in a register and not considering that
>it is a live reference for GC purposes, thus allowing it to be
>reclaimed while being used, then that is also a bug in the VM/GC.
>So I can see that indeed an object can 'disappear' while a method is
>executing upon it - but only if the object will in fact never be
>referenced again.
>So in the context of your original example: yes the finalizer could
>have cleared the static data associated with the object x, before the
>execution of gets to read it. It is, as you stated, a data
>race - though a subtle one.
>On the issue of synchronization I agree with you. It is not clearly
>stated, though I think it can be inferred, that an object upon which
>the monitor lock is held must be reachable - and hence is reachable at
>the time the lock is released.
>David Holmes
>JavaMemoryModel mailing list -

JavaMemoryModel mailing list -

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