On constant folding of final field loads

Aleksey Shipilev aleksey.shipilev at oracle.com
Tue Jun 30 09:49:32 UTC 2015


On 06/29/2015 04:10 PM, Vladimir Ivanov wrote:
> On 6/29/15 1:35 PM, Aleksey Shipilev wrote:
>> On 06/27/2015 04:27 AM, Vladimir Ivanov wrote:
>> Big picture question: do we actually care about propagating final field
>> values once the object escaped (and in this sense, available to be
>> introspected by the compiler)?
>> Java memory model does not guarantee the final field visibility when the
>> object had escaped. The very reason why deserialization works is because
>> the deserialized object had not yet been published.
>> That is, are we in line with the spec and general expectations by
>> folding the final values, *and* not deoptimizing on the store?
> Can you elaborate on your point and interaction with JMM a bit?
> Are you talking about not tracking constant folded final field values at
> all, since there are no guarantees by JMM such updates are visible?

Yup. AFAIU the JMM, there is no guarantees you would see the updated
value for final field after the object had leaked. So, spec-wise you may
just use the final field values as constants. I think the only reason
you have to do the dependency tracking is when constant folding depends
on instance identity.

So, my question is, do we knowingly make a goodwill call to deopt on
final field store, even though it is not required by spec? I am not
opposing the change, but I'd like us to understand the implications better.

For example, I can see the change gives rise to some interesting
low-level coding idioms, like:

final boolean running = true;
Field runningField = resolve(...); // reflective

// run stuff for minutes
void m() {
  while (running) { // compiler hoists, turns into while(true)
     // do stuff

void hammerTime() {
  runningField.set(this, false); // deopt, break the loop!

Once we allow users to go crazy like that, it would be cruel to
retract/break/change this behavior.

But I speculate those cases are not pervasive. By and large, people care
about final ops to jump through the barriers. For example, the final
load can be commonned through the acquires / control flow. See e.g.:

>>> Regarding alternative approaches to track the finality, an offset bitmap
>>> on per-class basis can be used (containing locations of final fields).
>>> Possible downsides are: (1) memory footprint (1/8th of instance size per
>>> class); and (2) more complex checking logic (load a relevant piece of a
>>> bitmap from a klass, instead of checking locally available offset
>>> cookie). The advantage is that it is completely transparent to a user:
>>> it doesn't change offset translation scheme.
>> I like this one. Paying with slightly larger memory footprint for API
>> compatibility sounds reasonable to me.
> I don't care about cases when Unsafe API is abused (e.g. raw memory
> writes on absolute address or arbitrary offset in an object). In the
> end, it's unsafe API, right? :-)

Yeah, but with millions of users, we are in a bit of a (implicit)
compatibility bind here ;)

> So, my next question is how to proceed. Does changing API and providing
> 2 set of functions working with absolute and encoded offsets solve the
> problem? Or leaving Unsafe as is (but clarifying the API) and migrating
> Reflection/j.l.i to VarHandles solve the problem? That's what I'm trying
> to understand.

I would think Reflection/j.l.i would eventually migrate to VarHandles
anyway. Paul? The interim solution for encoding final field flags
shouldn't leak into (even Unsafe) API, or at least should not break the
existing APIs.

I further think that an interim solution makes auxiliary single
Unsafe.fireDepChange(Field f / long addr) or something, and uses it
along with the Unsafe calls in Reflection/j.l.i, when wrappers know they
are dealing with final fields. In other words, should we try to reuse
the knowledge those wrappers already have, instead of trying to encode
the same knowledge into offset cookies?

>>> II. Managing relations between final fields and nmethods
>>> Another aspect is how expensive dependency checking becomes.

>> Isn't the underlying problem being the dependencies are searched
>> linearly? At least in ConstantFieldDep, can we compartmentalize the
>> dependencies by holder class in some sort of hash table?
> In some cases (when coarse-grained (per-class) tracking is used), linear
> traversal is fine, since all nmethods will be invalidated.
> In order to construct a more efficient data structure, you need a way to
> order or hash oops. The problem with that is oops aren't stable - they
> can change at any GC. So, either some stable value should be associated
> with them (System.identityHashCode()?) or dependency tables should be
> updated on every GC.

Yeah, like Symbol::_identity_hash.

> Unless existing machinery can be sped up to appropriate level, I
> wouldn't consider complicating things so much.

Okay. I just can't escape the feeling we keep band-aiding the linear
searches everywhere in VM on case-to-case basis, instead of providing
the asymptotic guarantees with better data structures.

> The 3 optimizations I initially proposed allow to isolate
> ConstantFieldDep from other kinds of dependencies, so dependency
> traversal speed will affect only final field writes. Which is acceptable
> IMO.

Except for an overwhelming number of cases where the final field stores
happen in the course of deserialization. What's particularly bad about
this scenario is that you wouldn't see the time burned in the VM unless
you employ the native profiler, as we discovered in Nashorn perf work.

Recapping the discussion in this thread, I think we would need to have a
more thorough performance work for this change, since it touches the
very core of the platform. I think many people outside the
hotspot-compiler-dev understand some corner intricacies of the problem
that we miss. JEP and outcry for public comments, maybe?


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: <http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/attachments/20150630/fca2e712/signature.asc>

More information about the hotspot-compiler-dev mailing list