[records] Ancillary fields

Brian Goetz brian.goetz at oracle.com
Wed Apr 18 20:39:12 UTC 2018



> Ahh, you missed the `lazy` keyword on there :-) Which is good because 
> it raises an issue: when you forget it, bad performance may result 
> without other observable consequence. Although, it's already the case 
> that reading code like the above ought to raise all kinds of alarm 
> bells (e.g., now I want to go check which fields computeHashCode() 
> might be referring to, and where /they're/ initialized), so I 
> /should/ be looking for that `lazy` keyword to put my mind at ease. So 
> maybe this is okay.

Well, "bad" is relative; it won't be any worse than what you do today 
with eager static fields.  But yes, I did drop the lazy there.

> I assume that, unlike other field initializers, I'm safe to refer 
> to/any/ other field regardless of how and where that field is 
> initialized. Right?

I think you mostly are asking about instance fields.  It would be safe 
to refer to any other field, however, if you _read_ a lazy field in the 
constructor, it might trigger computation of the field based on a 
partially initialized object.  The compiler could warn on the obvious 
cases where this happens, but of course it can be buried in a chain of 
method calls.

> The intersection with primitives is interesting. I assume it gets 
> secretly created as an Integer? So there's a little extra hidden 
> memory consumption.

For static fields, there's an obvious and good answer that is optimally 
time and space efficient with no anomalies: condy.  We desugar

     lazy static T t = e
     ...
     moo(t)

into

     // no field needed
     static t$init() { return ; }
     ...
     moo( ldc condy[ ... ] )

and let the constant pool do the lazy initialization and caching. JITs 
love this.

For instance fields, we have a choice; use extra space in the object to 
store the "already initialized" bit, or satisfy ourselves with the trick 
that String does with hashCode() -- allow redundant recomputation in the 
case where the initializer serves up the default value.

So I think the divide is not ref-vs-primitive but whether we are willing 
to take the recomputation hit when it serves up a default value.




More information about the amber-spec-observers mailing list