final field values should be trusted as constant (filed as JDK-8233873)
John Rose
john.r.rose at oracle.com
Wed Nov 13 21:23:35 UTC 2019
On Nov 9, 2019, at 11:21 AM, Doug Lea <dl at cs.oswego.edu> wrote:
>
> On 11/8/19 8:24 PM, John Rose wrote:
>
>> ## Side note on races
>>
>> Although race conditions (on non-volatile fields) allow the JVM some
>> latitute to return "stale" values for field references, such latitude
>> would usually be quite narrow, since an execution of the invalid
>> optimized method is likely to occur downstream of the invalidating
>> field update (as determined by the happens-before relation of the
>> JMM).
>
> Ever since initial revisions of JLS1 version, the intent of JMM specs
> (including current) is to allow compilers to believe that the value they
> see in initial reads of a final field is the only value they will ever
> see. So no revision is necessary on these grounds (although one of these
> days there will be one that accommodates VarHandle modes etc,
> formalizing http://gee.cs.oswego.edu/dl/html/j9mm.html). Some of the
> spec messiness exists just to explain why compilers are allowed not to
> believe this as well, because of reflection etc.
>
> In other words, don't let JMM concerns stop you from this worthwhile effort.
Thanks for the encouragement, Doug.
The JMM probably doesn’t require enhancement, but we do need some
elucidation of dark corners around settings of final fields outside if constructors.
Maybe it’s already been written; I’m sure it’s already been thought about,
by you at least. Setting a final inside a constructor is not problematic, as
long as a JIT can prove statically that a (non-static) field it proposes to
constant fold is in an instance whose constructor has returned (normally).
This all by itself is tricky to do (see also B, C below), but let’s set that aside.
Also tricky is smashing of finals of normally-constructed objects, which is
allowed by the JVM. This is allowed even if heinous. We need some
Big Hammer to turn off optimization when it happens. If we were really
slick we might try to amend the JMM to declare that the JIT can retain
a previously folded value, even after somebody smashed it, on the grounds
that this is a valid race condition, where the JITted code is racily reading
back in time. (Is this already true?? Probably not. It is worth considering
as a JMM enhancement; the idea is of races which are allowed to retain
sticky values that would ordinarily be updated. I’m thinking @Stable
and final both.) But set that aside for now also.
Here’s the hard part, I think.
When an instance is born without running a constructor, then we know
somebody did something off-label to set any non-static final fields it has.
This means the JMM makes no guarantees about safe publication of
those fields. This in turn means somebody must do something:
A. Give up on all static final fields, because somebody might smash
a properly constructed object. This is our decade-plus status quo,
which I’m very tired of.
B. Have the JVM leave enough clues to distinguish properly constructed
objects from the off-label ones. (This is the “larval/adult” distinction.)
Then the JIT can optimize only the normal cases and avoid the others.
C. Demand that frameworks which make off-label objects be upgraded
to include a memory fence after they are done setting their finals, so
that they conform to the JMM behaviors required of regular objects.
Make the fence be checkable by the JIT so it can see when the
framework has done its duty, so the JIT can treat the object normally.
D. Have the JMM make special rules for final fields, that their values
are “sticker” or more “memorable” than normal fields, so races can
see old values *if the JVM want to*.
E. Some balanced combination of the above. I.e., give frameworks
a carrot for upgrading their construction sequences with better
performance, and a stick of emitting log entries or warnings when
they spew out objects in the larval state.
F. Your idea here, please!
— John
More information about the hotspot-compiler-dev
mailing list