Review Request: Add ClassOption.STRONG and default is unspecified
john.r.rose at oracle.com
Fri Mar 13 00:35:50 UTC 2020
On Mar 11, 2020, at 3:34 PM, David Holmes <david.holmes at oracle.com> wrote:
> But I much prefer what was suggested previously: either default is strong and you can ask for weak; or default is weak and you can ask for strong.
I’m less averse to a tri-state option, but I agree it’s awkward.
As I said before, WEAK is the natural semantics, which means
that it should be the default. STRONG is a special semantics
which links the HC to its CL; STRONG is desirable in cases
where the creator of the HC has special knowledge that the
HC will in fact be “rooted” (in the GC sense) in some part of
a class belonging to the CL. Any use of invokedynamic is
like this, since the indy makes a durable reference from the
call site to the HC that implements it. But notice this:
When a LMF creates a HC for a lambda, there is no way to
know that this HC will be linked permanently to an indy
instruction, other than asking the author of the LMF.
That’s why the author needs to say, “this HC will be
permanently installed where I’m putting it”. And in
that case, if the LMF author knows that STRONG gets
slightly better footprint, the LMF author should ask
for STRONG mode.
Here’s a key point: Because WEAK mode is the natural mode
for Has, it would be correct (but less efficient) for the LMF
author to accept WEAK mode. Setting STRONG model later
on would be a compatible change improving performance.
Let’s turn that key point around: Suppose you have a very
data-driven system (as opposed to a class-driven one) more
like Nashorn. In that case, HCs can be used to customize
behaviors, but they will typically be stored in lookup tables,
not attached to call sites in classes. (Nashorn does both IIRC.)
In particular, HCs which are shared between classes, which
is a common tactic (in java.lang.invoke, for example!) are
stored not in classes but in tables. Which CL should those
guys be attached to? There’s no right answer, but HCs don’t
care about they; their natural behavior is to go dead as soon
as the last client drops a reference to them.
Let’s pretend for a moment that the option is a straight-up
boolean argument, to back away from “who gets the good
syntax”. Note that there are class-based and data-driven
uses of HCs. What are the costs and benefits of setting
the switch either way in both cases?
use mode correct? performant?
class WEAK yes footprint cost
class STRONG yes(*) yes
data WEAK yes footprint cost
data STRONG NO OOME
What do we learn from this? The behavior I’m calling
“natural” (WEAK mode) is natural because it is correct
in all cases, although some cases, given special advice
from the coder, have a more performant option.
The unnatural STRONG mode is unnatural because
some valid uses of the HC mechanism will fill up the
heap with still-loaded, unusable HC bodies until an
OOME. The OOME is not because the HCs were used
according to spec, it is because the JVM has shirked
its duty to get rid of them when they are no longer
needed. The star (*) above means, “yes, this behavior
is correct, but only in this very special case”—where
the programmer promises (correctly) that the HC will
be used as long as the LC (or some other CL-based
class) is in existence.
That’s what I mean by natural. If you use STRONG
mode on a HC, you introduce a new failure mode
(OOME) not part of the overall HC contract. I suppose
you could say “No, John, HC’s *naturally* accumulate
on their LC until OOME, an *that’s* the true natural
behavior.” But then WEAK becomes *another*
natural behavior, of “free-range” HCs. The more
economical specification here is to say that HCs
are normally free-range (managed by the GC and
unloaded when all uses disappear), but a special
option ties them *additionally* (not *instead*!!)
to the CL of their LC.
OK, so now how to set the default? The answer
should be easy, and here it is: Set the default so
that the behavior of the LC is always as correct
as possible, given no further contextual information.
The non-default will be a setting which can sometimes
be the wrong decision, but may provide special
benefits in special situations.
The old saying for coders is “first make it work,
then make it fast”. WEAK mode *always* makes
it work, while STRONG mode *sometimes* makes
it fast (and other times makes it wrong).
So, get rid of WEAK and add STRONG. And code
all of our class-based (indy-serving) metafactories
to specify STRONG.
I’m glad to get this off my chest. I’ve hated the
setting of the default to STRONG ever since this
project started, but saw it as a losing battle because
folks were looking at the implementation (where
the easy setting is STRONG and WEAK is annoying
extra work). But I’m the inventor of this thing,
and you can trust me to know what are the set
of valid use cases (and not just the current ones).
More information about the valhalla-dev