Lambda vs anon class optimization effect on CHA

Vitaly Davidovich vitalyd at gmail.com
Wed Aug 10 21:08:16 UTC 2016


Thanks for the reply Aleksey; a few comments below ...

On Wed, Aug 10, 2016 at 3:02 PM, Aleksey Shipilev <
aleksey.shipilev at gmail.com> wrote:

> On 08/10/2016 04:28 PM, Vitaly Davidovich wrote:
> > public abstract class Foo {
> >     public static java.util.function.Supplier<Foo> call() {
> >         return () -> Confuser.INSTANCE;
> >     }
> > }
> >
> > public final class Impl extends Foo {}
> >
> > public final class Confuser extends Foo {
> >       public static final Confuser INSTANCE = new Confuser();
> >
> > }
>
> I renamed "Null" to "Confuser" and "null()" to "call()" above.
>
Sure.  The Null here was basically a case of the "Null Object" pattern, but
any naming will do, of course.

>
>
> > Then CHA kicks in.  Keep in mind that the code I'm running never invokes
> > call() and only instantiates Impl, so the expectation is that Confuser
> would
> > not load (and disable the single impl CHA optimization).
> >
> > My understanding was that the lambda in call() would have a bootstrap
> > method that invokes the lambda metafactory to actually bind the call to
> > the synthetic static method that's generated, which in turn actually
> > references Confuser.INSTANCE (and would cause the class to load).
> However,
> > I'm clearly missing or misunderstanding something.
> >
> > Would anyone be able to explain the above situation? Is that expected?
> > Let me know if you need more info.
>
> This has nothing to do with CHA per se.

Well, sort of.  As you say at the end, perhaps CHA can be wired up to look
for a less eager dependency, so it seems relevant to CHA.

> Your question boils down to why
> a dependent class is resolved earlier. AFAIU, "Confuser" class is
> getting loaded when we verify Foo class. JDK 9b128 with
> -Xlog:class*=debug prints this:
>
> [0.917s][info ][class,init       ] Start class verification for: Foo
> [0.917s][debug][class,resolve    ] Foo Foo Foo.java (verification)
> [0.942s][debug][class,resolve    ] Confuser Foo (super)
> [0.942s][debug][class,resolve    ] Foo Confuser Foo.java (verification)
> [0.942s][info ][class,init       ] End class verification for: Foo
>
> Running with -Xverify:none avoids resolving the Confuser class. Now, the
> question is why verifier triggers the class resolution.
>
> I would go on and speculate this happens because we verify the lambda
> body code, which happens to reside in the same Foo class:
>
>   private static Foo lambda$call$0();
>     descriptor: ()LFoo;
>     flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
>     Code:
>       stack=1, locals=0, args_size=0
>          0: getstatic     #3                  // Field
> Confuser.INSTANCE:LConfuser;
>          3: areturn
>       LineNumberTable:
>         line 3: 0
>
> We have to verify that Confuser is indeed a subclass of Foo here, so the
> value read from field is assignable to a return type, right?
>
> If we drop "extends Foo" from Confuser and make lambda return Object,
> then we don't resolve the class. If we make Confuser.INSTANCE the Foo
> type, not Confuser, we don't resolve the class either. If we replace
> lambda with an anonymous class, then the body is in a separate .class,
> and we do not resolve again.
>
Interesting -- I hadn't tried the variants of making lambda return Object
or making the INSTANCE field be of type Foo.  It also didn't occur to me
that the placement of the body matters (in the declaration class in case of
lambda and different class in case of anon class).  I always thought that
verification is fairly "late bound".

>
> Ultimately, I think JVMS allows resolving the class before referencing
> it (in contrast with _initialization_, which should happen no earlier
> than a first reference at run time) -- like in the example above, to
> read class metadata to aid verification.
>
> I would venture to guess that we can wire up CHA to look at what classes
> have been _initialized_ rather than what classes were _resolved_, but
> there is no telling how much bugs would that expose.
>
 Right.  Intuitively, one would want the CHA dependency registered at the
latest possible stage before the JVM cannot guarantee that only a single
receiver is possible at all callsites making use of this optimization.  But
yeah, no clue what bugs/side-effects/anomalies that may shake out.

I was slightly puzzled (and disappointed) that using lambda like this
cancels out the single impl CHA optimization, but at least now I have an
explanation, so thanks for that.

>
> Thanks,
> -Aleksey
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/attachments/20160810/292da499/attachment.html>


More information about the hotspot-compiler-dev mailing list