RFR(XL) 8198698: Support Lambda proxy classes in dynamic CDS archive

Mandy Chung mandy.chung at oracle.com
Tue Jun 2 18:38:24 UTC 2020


Hi Calvin,

To recap an offline discussion, I think we should use JDK bootcycle 
build to test this feature.  For example generating a boot JDK with 
archived lambda proxy classes for java.base and javac to build itself 
and also use it to run JDK tier 1-3 tests.  This would serve a good test 
bed.   I also suggest to experiment how JDK can benefit from this 
feature (e.g.  any java.base and javac performance gain). The module 
system initialization had to avoid using lambdas at startup time and we 
could re-examine this again.

On 5/29/20 2:29 PM, Calvin Cheung wrote:
> RFE: https://bugs.openjdk.java.net/browse/JDK-8198698
>
> webrev: http://cr.openjdk.java.net/~ccheung/jdk15/8198698/webrev.00/
>

Hidden classes have the following properties:
1. not discoverable by name.  It's not registered in the system dictionary.
2. It can be a dynamic nest member of a nest host.  For lambda proxy 
class, it has the same nest host as the caller class (same runtime 
package as the caller class)
3. strong or weak link with the class loader.  a hidden class may be 
unloaded even its defining class loader is alive.

For the archived lambda proxy classes, do you re-assign a new class name 
to the shared lambda proxy class?  I suspect not.  It's very rare that 
it will conflict with the name of other hidden classes. It's an option 
to patch the name to indicate it's from shared archive if it helps 
diagnosability.

My comments below are related to the above.

1374 InstanceKlass* 
SystemDictionary::load_shared_lambda_proxy_class(InstanceKlass* ik,
1375 Handle class_loader,
1376 Handle protection_domain,
1377 PackageEntry* pkg_entry,
1378 TRAPS) {
1379 InstanceKlass* shared_n_h = 
SystemDictionaryShared::get_shared_nest_host(ik);
1380 assert(shared_n_h->is_shared(), "nest host must be in CDS archive");
1381 Symbol* cn = shared_n_h->name();
1382 Klass *s = resolve_or_fail(cn, class_loader, protection_domain, 
true, CHECK_NULL);
1383 if (s != shared_n_h) {
1384 // The dynamically resolved nest_host is not the same as the one we 
used during dump time,
1385 // so we cannot use ik.
1386 return NULL;The above resolve_or_fail is to ensure that H is the 
same class loaded by the given class loader.  Another validation that you should do is to check
the nest host and nest member must be in the same runtime package -
maybeik->set_nest_host(shared_n_h, THREAD) validates the runtime package??


SystemDictionaryShared::load_shared_lambda_proxy_class
  
1679   InstanceKlass* shared_n_h = get_shared_nest_host(lambda_ik);
1680   assert(shared_n_h != NULL, "unexpected NULL _nest_host");
1681
1682   InstanceKlass* loaded_lambda =
1683     SystemDictionary::load_shared_lambda_proxy_class(lambda_ik, class_loader, protection_domain, pkg_entry, CHECK_NULL);
1684
1685   InstanceKlass* n_h = caller_ik->nest_host(THREAD);
1686   assert(n_h->has_nest_member(caller_ik, THREAD) ||
1687          n_h == caller_ik, "mismatched nest host");

I think you meant to check n_h->has_nest_member(loaded_lambda, THREAD).

It'd be good to add a comment to say that this ensures thatthe caller's nest host must be the same as the lambda proxy's nest host 
recorded at dump time.
Nit: the variable name "n_h" or "_n_h" would be clearer if it's named
"nest_host" or "_nest_host".
  
1689   SystemDictionary::set_shared_class_init_state(lambda_ik, InstanceKlass::loaded);

A lambda proxy class typically is eagerly initialized (unless a flag is given).
Your change dumps the hidden class with no knowledge if `Lookup::defineHiddenClass`
is called with "initialize" parameter is true or not.  This may affect the runtime
performance (for example MethodHandle will have class init barrier if the class
is not yet initialized).
This may have performance implication. I think we should consider 
passing the initialize parameter to LambdaProxyClassArchive::find such 
that if the class is found from CDS archive, it will invoke <clinit> 
before it returns to the library. One other point I brought up - strong 
vs weak hidden class (i.e. is it using the primary CLD or the per-mirror 
CLD?). I have lost myself where you do this in the implementation. Can 
you point that out? Is it recorded in the shared archive and make sure 
it uses the exact CLD when it's loaded?

JVM_RegisterLambdaProxyClassForArchiving - we can add an assert to check 
if lambdaProxyClass is a hidden class. JVM_IsCDSDumpingEnabled and 
JVM_IsCDSSharingEnabled - an alternative library/VM interface is to use 
internal system properties that jdk.internal.misc.VM::saveProperties 
reads and removes these flags. InnerClassLambdaMetafactory 247 /** Use 
the LambdaProxyClassArchive for including the lambda proxy class 248 * 
in CDS archive and retrieving the class from the archive. If the class 
249 * is not found in the archive, it calls generatesInnerClass to 
create 250 * the class. 251 */ What about simpler comment: /* * Spins 
the lambda proxy class. * * This first checks if a lambda proxy class 
can be loaded from CDS archive. * Otherwise, generate the lambda proxy 
class. */ This adds a VM call for every lambda proxy class creation. Do 
you have any the performance measurement when CDS is not on? Any 
performance regression? Mandy



More information about the hotspot-runtime-dev mailing list