[concurrency-interest] [Javamemorymodel-discussion] Fences.keepAlive

Boehm, Hans hans.boehm at hp.com
Tue Jan 20 18:44:28 EST 2009


 

> -----Original Message-----
> From: Eger, Patrick [mailto:peger at automotive.com] 
> Sent: Tuesday, January 20, 2009 3:08 PM
> To: Boehm, Hans; Alexander Terekhov
> Cc: Doug Lea; Concurrency-interest at cs.oswego.edu; 
> javamemorymodel-discussion at cs.umd.edu
> Subject: RE: [concurrency-interest] 
> [Javamemorymodel-discussion] Fences.keepAlive
> 
> Curious, why not either:
> 
> A)
> Change the Java semantics to be more C++ like then? JVMs 
> could simply keep an extra object pointer on the method stack 
> frame for "this" to prevent GC so the implementation could 
> likely be quite simple. Correct finalization of the below 
> datastructure would require synchronization of some sort 
> anyways, so the compiler/memory ordering barriers will 
> already likely be there preventing optimizations that could 
> otherwise be made.
> finalize()-free classes of course would not have any penalty 
> at all since the JIT could detect this. Importantly, I think 
> the amount of code that absolutely relies on GC to cleanup 
> memory immediately (for
> correctness) is little to non-existent. I may be off-base 
> here but it seems likely that more code would actually be 
> fixed by this change than broken. Dunno if this is acceptable or not.
I think it's very tricky to do this without essentially prohibiting all dead variable elimination on references to Java objects.  In particular, limiting it to just "this" seems very weird to me.  It fixes the most obvious examples.  But if someone rewrites one of those member functions as a static function with an explicit object parameter, the code breaks again.  That would probably be even harder to explain than the original problem.

Solution 2 in http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2007/n2261.html tries to contain the effect to objects that appear finalizable.  There's a handwaving, untested argument that that might make sense, but even that's unclear.  The problem is that a reference to a non-finalizable P may actually refer to a subclasss that is finalizable. And thinking about it again, it almost certainly doesn't work in Java, since java.lang.ref effectively allows you to finalize any object, no matter what its type.

It seems likely to me that the only clean solution would be to effectively prohibit all dead variable elimination, effectively allowing it only when provably can have no effect on finalization or reference enqueuing, and would hence be invisible (except that space requirements may be reduced.)  In addition to increased register pressure in the compiler, this has some cost in extra garbage retention by the collector.

> 
> 
> Or alternatively, 
> 
> B)
> Have the JIT implicitly insert a keepAlive(this) directly 
> before the method return, for all classes having a non-empty 
> finalizer, in all methods that access the "this" reference. 
> This would have the advantage of "fixing" these broken 
> programs without possible breakage. The only issue I can see 
> here would be the possible performance impact.
> Mitigating this i see a couple things: a) keepAlive(this) 
> could be a compiler intrinsic and so multiple keepAlive() 
> "calls" could be collapsed when inlining, etc. b) Coders have 
> been warned against
> finalize() strongly for so long that this would only effect 
> those few classes for which they are already expecting a 
> performance hit.
I think this is essentially equivalent to (A), with the same issues.

Again, the performance hit is difficult to limit to classes that use finalization.  C++ does manage to limit it to dead variable elimination on objects with destructors.  But once you make heavy use of something like C++ shared_ptr, there are a lot of those objects.

Hans

> 
> 
> Thoughts? Am I being naive here?
> 
> 
> > -----Original Message-----
> > From: concurrency-interest-bounces at cs.oswego.edu 
> [mailto:concurrency- 
> > interest-bounces at cs.oswego.edu] On Behalf Of Boehm, Hans
> > Sent: Tuesday, January 20, 2009 1:29 PM
> > To: Alexander Terekhov
> > Cc: Doug Lea; Concurrency-interest at cs.oswego.edu; javamemorymodel- 
> > discussion at cs.umd.edu
> > Subject: Re: [concurrency-interest] [Javamemorymodel-discussion] 
> > Fences.keepAlive
> > 
> > > From: Alexander Terekhov [mailto:alexander.terekhov at gmail.com]
> > >
> > > On Tue, Jan 20, 2009 at 8:20 PM, Boehm, Hans <hans.boehm at hp.com> 
> > > wrote:
> > > > I'd be inclined to somewhat simplify the example, and 
> not use the 
> > > > constructor.  I'm also not sure I'd mention the word
> > > "native", since
> > > > that seems to mislead people to believe that this is a 
> native code 
> > > > issue.  Something like:
> > > >
> > > > * class Resource {
> > > > *   static Resource[] externalResourceArray = ...
> > > > *   int myIndex;
> > > > *   Resource(...) {
> > > > *     myIndex = ...
> > > > *     externalResourceArray[myIndex] = ...;
> > > > *     ...
> > > > *   }
> > > > *   protected void finalize() {
> > > > *     externalResourceArray[myIndex] = null;
> > > > *     ...
> > > > *   }
> > > > *   void foo() {  // This might be the last use of this object.
> > > > *      int i = myIndex;
> > > > *      //  Without keepAlive, this object could be finalized
> > > > *      //  here, and externalResoureArray[i] could be
> > > > *      //  cleared, since the object itself may not otherwise
> > > > *      //  be needed; if there are no later accesses, only
> > > > *      //  the index i is still live.
> > > > *      bar(externalResourceArray[i]);
> > > > *      keepAlive(this);
> > > > *   }
> > > > * }
> > >
> > > Q: What does it mean to have no GC?
> > > A: You must write "delete" (C++ locals aside for a moment) at 
> > > destruction point(s).
> > >
> > > Q: What does it mean to have a GC?
> > > A: You must write "keepAlive" in every member function possibly 
> > > affected by the GC's premature destruction of *this.
> > >
> > The latter of course only applies to finalizable objects, of which
> there
> > should be very few.
> > 
> > The real difference between say, the C++ and Java 
> approaches here is 
> > actually not so much the presence of GC.  C++ shared_ptr 
> fundamentally
> has
> > the same issues, in some ways more so.  It seems to me that
> interestingly
> > the difference is that C++ effectively went for simpler 
> semantics and
> less
> > performance here, while Java went for more performance at 
> the expense
> of
> > programmability.  C++ delays destruction of locals until the end of
> the
> > block, thus prohibiting dead variable elimination where it might be 
> > visible.  Hence a shared_ptr and the like can't be destroyed early.
> Java
> > allows pointers to disappear early in ways that are visible to
> finalizers.
> > This makes at least some small amount of sense, since C++ 
> destructors
> are
> > intended to be much more common that Java finalizers, and the
> performance
> > impact would still be more or less universal in Java.  
> Whether it is 
> > really the right decision for Java, I don't know.  But I think the 
> > decision was effectively (probably unintentionall!
> >  y?) made around the Java 1.1 time frame, and changing it 
> is likely to
> be
> > nontrivial.
> > 
> > Hans
> > _______________________________________________
> > Concurrency-interest mailing list
> > Concurrency-interest at cs.oswego.edu
> > http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> 


More information about the Concurrency-interest mailing list