Lambda vs anon class optimization effect on CHA

Aleksey Shipilev aleksey.shipilev at gmail.com
Wed Aug 10 19:02:22 UTC 2016


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.


> 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. 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.

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.

Thanks,
-Aleksey

-------------- 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/20160810/eca3cccf/signature.asc>


More information about the hotspot-compiler-dev mailing list