RFR: JDK-8222373 Improve CDS performance for custom class loaders

Remi Forax forax at univ-mlv.fr
Tue Jun 25 07:17:32 UTC 2019



----- Mail original -----
> De: "John Rose" <john.r.rose at oracle.com>
> À: "Ioi Lam" <ioi.lam at oracle.com>
> Cc: "core-libs-dev" <core-libs-dev at openjdk.java.net>, "hotspot-runtime-dev" <hotspot-runtime-dev at openjdk.java.net>
> Envoyé: Lundi 24 Juin 2019 19:13:04
> Objet: Re: RFR: JDK-8222373 Improve CDS performance for custom class loaders

> On Jun 20, 2019, at 12:12 AM, Ioi Lam <ioi.lam at oracle.com> wrote:
>> ...
>> I have a rough idea -- let's have a higher-level representation of the bytecode
>> stream than byte[] or ByteBuffer, to make optimization possible.
> 
> Template classes will need something similar, so maybe there is a common design
> point in here.
> 
> The key feature of template classes is that their loading sequence is split into
> parts:
> 
> 1. Load the template, defining an abstract API and preprocessing incomplete
> parts.
> 2. Specialize the loaded template, completing all parts, loading a specialized
> class ("species").
> 
> Step 1 happens once per template.  Step 2 can happen any number of types, with
> varying template parameters.

types -> times

> 
> I think that, at least in the internals, there will be a BSM-like (now, that's a
> surprise) template specialization function (TSF) declared in step 1 and
> executed in step 2.

It has to be a BSM-like otherwise we will bolt the Java the language generic semantics into the template specialization mechanism.
Given that we want to support any languages generics (even the ones that doesn't exist yet), we have to ask how the substitution is done.

> 
> The TSF will need to consult the results of step one and request the JVM to
> combine them into the desired species.  Ad hoc logic may be involved, such as
> detecting if T in List<T> is Comparable and mixing in Comparable into the
> resulting List, with a standard comparison method (e.g. lexicographic compare
> lifted from the elemental compares).
> 
> Key questions:
> 
> A. What API reflects the template chunks loaded in step 1?  Probably not just
> byte streams, more like what you are reaching for here, Ioi.
> 
> B. What API loads the customized chunks?  Probably (again) not today's
> ClassLoader which requires byte streams.
> 
> I'm slowly working on an answer to A, in the form of a "class excavator" which
> pulls out structural information from live JVM metadata, without "deadening" it
> into a byte stream.  Not ready for prime time, but the basic idea is to present
> the logical schema of the loaded class file (in terms of the original classfile
> structure) in a style similar to the existing constant pool reflection API
> (which is JDK internal).

yes, once we have live metadata, we can not go back and try to shove them into the byte stream, otherwise there will be a kind of "runtime erasure".

> 
> Once we get a fuller answer for A we can try to invert it to get an answer for
> B.  That is, if we know what we'd like to see in the JVM (via the excavator) we
> want to tell the JVM to establish some new species definition, related to the
> excavated data.
> 
> For most JDK work we can wrap low-level excavator/establisher mechanisms (which
> don't need to be very O-O) in a thin layer of value types and interfaces.
> 
> I'm using new terms to help me think of this as a new kind of API, not just a
> small variation on class loading or reflection.  The new feature is that the
> reflective part comes first, and is followed (as a sort of reversed operation)
> by the loading part.  Also new is that we don't want to abstract everything
> through serialized byte streams, since they make it very hard to share
> features—but (I think) species need to share template metadata, not just make
> copies of it.

other open questions:
 - what are the metadata we need to expose, constant pool constants, obviously, but also vtable slot and field layout slot ?
 - what are the checks performed by the VM we a live value is stored as metadata, a covariant checks seems to be the answer ? 

> 
> — John

Rémi

> 
>> So we could have a new API in ClassLoader
>> 
>>     protected final Class<?> defineClass(String name, String location,
>> ProtectionDomain protectionDomain)
>> 
>> and its behavior is equivalent to the following
>> 
>>    {
>>         byte[] b = read_buffer_from(location);
>>         return defineClass(name, b, 0, b.len, protectionDomain);
>>    }
>> 
>> 
>> examples would be:
>> 
>>      defineClass("java/lang/Object", "jrt:/java.base/java/lang/Object.class", NULL);
>>      defineClass("com/foo/Bar", "file://path/com.foo.jar!com/foo/Bar.class",
>>      myProtectionDomain);
>> 
>> Note that the type and value of <location> is just for illustrative purposes. We
>> might use a different type (URI??). It's just a way to name a location that you
>> can read a byte buffer from.
>> 
>> The protectionDomain will need to be created by the caller. The use of the
>> protectionDomain will be no different than the existing defineClass() APIs.
>> Specifically, it will not be used in any way to fetch the buffer.
>> 
>> When CDS is enabled, the VM can check if the name+location matches a class in
>> the CDS archive. If so, the class is loaded without fetching the buffer.
>> 
>> The caller doesn't need to know if CDS is enabled or not.
>> 
>> 
>> (We probably don't want a String type but a more abstract type. That way we can
>> evolve it to allow more complex representations, such as "read the bytecode
>> stream from here, but replace the method name "Foo" to "Bar", and add a new
>> integer field "X" ....
>> 
>> If John Rose was to design this, I am sure he will call it something like
>> BytecodeStreamDynamic :-)
>> 
>> This may actually reduce the use of ASM. E.g., today people are forced to write
>> their own bytecodes, even if they just want some simple transformation on
>> template classes).
>> 
>> 
>> Thanks
> > - Ioi


More information about the core-libs-dev mailing list