[concurrency-interest] lazy finals - was: Here's why Atomic*FieldReference access checking is broken

Vladimir Ivanov vladimir.x.ivanov at oracle.com
Fri Oct 10 11:04:20 EDT 2014


Peter,

 >> I don't see how it improves the situation, except allowing lazy
>> initialization. It suffers from the very same problems as final
>> fields, if you want to perform multiple updates.
>
> But since they are new kind of fields, multiple updates can be
> prohibited on them from day one. Who would want such a feature anyway.
> Lazy setting yes, but not multiple updates. Normal fields are meant to
> be updated multiple times, finals are special-purpose fields.
That's true. My point was about changing final fields using Reflection 
API. It doesn't prohibit multiple updates of the same final field. For 
lazy finals it should be forbidden.

>> And the price for lazy initialization is default value, which can't be
>> optimized anymore.
>
> That's a pity yes, but as I understand, lazy-initialized finals are
> expected to be assigned a non-default value before they are used in hot
> code which can then benefit from constant folding, right?
Exactly. The problem is when the final value coincides with default 
value, but there are workarounds (use a wrapper/box).

>>> But what about this idea:
>>>
>>> Reflection supports updating ordinary final instance fields (via
>>> privileged setAccessible(true)) in order to achieve just what the lazy
>>> final fields are trying to achieve - lazy initialization outside
>>> constructor. Mainly to support Java  and other kinds of
>>> deserializations. I would say that any other uses of setAccessible(true)
>>> which assigns final field more than once are rare or non-existent. So
>>> why not making ordinary instance final fields *really* final by treating
>>> them in reflection as lazy final fields with a little tweak - the
>>> assignment of null/zero value to an "unassigned" field would not throw
>>> exception, but just be a NOOP - leave the field in "unassinged" state.
>>>
>>> This way deserialization of ordinary final fields would still work, but
>>> VM compiler could treat them all as @Stable, immediately optimizing huge
>>> codebases on a different level.
>>>
>>> Am I not seeing something in that simplified picture?
>> It's all about JIT compiler. Lazy finals/@Stable aren't a full match
>> for final field case, but they are close.
>>
>> The problem with treating final fields as lazy finals, is it can't be
>> limited only to accesses through Reflection API. The change should be
>> pervasive and default values should be treated specially everywhere,
>> forbidding constant-folding of loads from final fields w/ default
>> values. Do we really want that?
>
> Well, it would not be worse than it is now when constant-folding can't
> be performed regardless of final field's value. null/zero value would
> become a second-class citizen then. Not very nice, but perhaps acceptable.
>
>>
>> Regarding constant folding of loads from final fields, Hotspot already
>> optimizes loads for static final fields and there's an experimental
>> flag TrustFinalNonStaticFields for final instance fields case.
>
> So for static final fields, Hotspot already knows how to prove that some
> code can not observe uninitialized value before constant-folding it?
> Does it do any such thing for instance fields when this experimental
> flag is enabled or just trusts blindly that the code in question is only
> observing the "final" value when the time comes to optimize it? In other
> words, does this experimental flag guarantee correctness in all
> situations with the only exception being reflective final field updates?
HotSpot doesn't do anything special for both static & instance final 
fields. So, if you change them through Reflection API, you are on your 
own - in some situations some updates can go unnoticed by some parts of 
the application.

>> What HotSpot misses right now (to preserve correctness w.r.t.
>> Reflection API) is a way to track dependencies between final fields
>> and nmethods which embed their values. It would allow to invalidate
>> all nmethods which rely on stale value and ensure the updated value is
>> "visible" everywhere in the running application.
>
> This would be a cool optimization for mostly read-only and rarely
> updated fields, but do we want to transform final instance fields into
> such thing just to support deserialization via reflection? If this is
> not a big deal to support than perhaps it is a better approach since it
> does not neglect null/zero values.
DI & serialization shouldn't cause too much trouble IMO. IIUC, they 
mostly modify newly created objects which haven't been published yet.
So, there shouldn't be any embedded values to care about.

For corner cases of highly mutable final fields, VM should avoid 
optimizing them after a number of unsuccessful attempts.

Best regards,
Vladimir Ivanov


More information about the Concurrency-interest mailing list