Conversation
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
…s in the assembly phase Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
|
👋 Welcome back asmehra! A progress list of the required criteria for merging this PR into |
|
❗ This change is not yet ready to be integrated. |
|
@ashu-mehra this pull request can not be integrated into git checkout custom-loader-support-v2
git fetch https://git.openjdk.org/leyden.git premain
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge premain"
git push |
iklam
left a comment
There was a problem hiding this comment.
Hi Ashutosh, thanks for working on the POC. It looks very promising.
I have some suggesting for loading the classes in the assembly phase.
Does this work with simple test cases where a single URLClassLoader is being used?
It would be great if you could merge your code with the latest Leyden repo so other can play with it -- if you're ready for that ... :-)
| } else if (k->is_defined_by_aot_safe_custom_loader()) { | ||
| // Use UnregisteredClassLoader to load these classes | ||
| Handle unreg_class_loader = UnregisteredClasses::unregistered_class_loader(THREAD); |
There was a problem hiding this comment.
unregistered_class_loader() can define only one class for each name. I think we should create one ClassLoader instance for each registered AOT compatible loader.
Also, we should rename UnregisteredClassLoader in CDS.java to something like StaticClassLoader, and use it for both the existing "unregistered" classes, as well as classes for new "registered AOT compatible loaders".
| // Use UnregisteredClassLoader to load these classes | ||
| Handle unreg_class_loader = UnregisteredClasses::unregistered_class_loader(THREAD); | ||
| assert(unreg_class_loader.not_null(), "must be"); | ||
| Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), unreg_class_loader, true, CHECK); |
There was a problem hiding this comment.
Instead of using _all_klasses, I think we can create one list for each registered loader.
We order the loaders by delegation (parent loaders are processed first).
Each list is sorted by class hierarchy (similar to what happens in AOTLinkedClassBulkLoader).
Then, we can directly call into SystemDictionary::preload_class(loader, k). Doing the name lookup in SystemDictionaryShared::load_shared_class_for_aot_safe_custom_loader() seems necessary. Also, such an API would open up an execution path that might be taken inadvertently, so it's better to avoid it.
By the way, classes in _all_klasses are loaded even if AOTClassLinking is false. For simplicity, I think we should support registered loaders only when AOTClassLinking is true.
There was a problem hiding this comment.
I was wondering about the same. We should be able to use SystemDictionary::preload_class(loader, k for loading classes in assembly phase as well. The preloading in production run and in assembly phase should be able to use the same code. I will work on this change.
There was a problem hiding this comment.
Instead of using _all_klasses, I think we can create one list for each registered loader.
We order the loaders by delegation (parent loaders are processed first).
I think in FinalImageRecipes, used during assembly phase, we don't need to maintain list of each registered loader if we can process the classes in hierarchical order. In 4711732 I updated ArchiveBuilder to store the classes in hierarchical order, and use the sorted class list to create class tables, similar to AOTLinkedClassTable. I also clubbed unregistered classes and aot-safe loader classes into a single category of custom_loader_classes. We don't really need to distinguish between the two cases during assembly phase. All these classes get loaded via SystemDictionary::preload_class() (with some modifications).
This change removed the need to do the lookup through SystemDictionaryShared::load_shared_class_for_aot_safe_custom_loader().
Does this make sense?
There was a problem hiding this comment.
Can you handle a situation like this?
URLClassLoader a = foo.jar:bar.jar; loads classes Foo and and Bar
URLClassLoader b = foo.jar:taz.jar; loads classes Foo and and Taz
In the assembly phase, you have only a single class loader instance,UnregisteredClasses::unregistered_class_loader(), that loads all the custom loader classes, I think it won't be able to define two different Foo classes:
InstanceKlass* loaded_ik = SystemDictionary::find_instance_klass(THREAD,
ik->name(), loader);
if (loaded_ik == nullptr) {
SystemDictionary::preload_class(loader, ik, CHECK);
} else {
assert(loaded_ik == ik, "sanity check"); <<<<< HERE
}
Also, do you plan to support more complex cases like delegation?
URLClassLoader a = foo.jar, parent = system loader;
URLClassLoader b = bar.jar. parent = a;
a: load class Foo
b: load class Bar, whose super class is Foo
There was a problem hiding this comment.
Can you handle a situation like this?
URLClassLoader a = foo.jar:bar.jar; loads classes Foo and and Bar
URLClassLoader b = foo.jar:taz.jar; loads classes Foo and and Taz
Nope, this won't be handled with the current state of things. I think we are not handling this scenario in the training phase either. Only one InstanceKlass for class Foo would be stored in ArchiveBuilder::_klasses list.. The other one would be marked for exclusion. If we want to allow this, then we will have to allow duplicates in the ArchiveBuilder::_klasses list and then update FinalImageRecipe to maintain a list of each registered loader, as you mentioned earlier. Seems possible. I will take a stab at this.
Also, do you plan to support more complex cases like delegation?
Yes, I expect the simple parent-first delegation model to work, but I haven't tested it yet. I will check it.
There was a problem hiding this comment.
Only one InstanceKlass for class Foo would be stored in ArchiveBuilder::_klasses list.
This limitation of unique name should apply only to the "unregistered" classes. We current have two categories of classes: BUILTIN and UNREGISTERED:
We have the odd UNREGISTERED name because we once had REGISTERED long time ago (but this was never published in open-JDK). We had an API to register a class loader for CDS support (it's kind of similar to what you're doing) but we never had a satisfactory solution, so it was abandoned when the AppCDS code was added in JDK 8.
If it makes sense, you can add this enum back.
Another option is to get rid of the name "unregistered" altogether, and use something line BUILTIN vs CUSTOM.
The CUSTOM classes must be unique with the (aot-id + name) combination. A CUSTOM class that's not defined by an aot-safe loader will have an aot-id of null. This way we will retain the old behavior of UNREGISTERED classes.
There was a problem hiding this comment.
Another option is to get rid of the name "unregistered" altogether, and use something line BUILTIN vs CUSTOM.
This is exactly what I was planning to do. UNREGISTERED doesn't convey the intention in current code context.
The CUSTOM classes must be unique with the (aot-id + name) combination. A CUSTOM class that's not defined by an aot-safe loader will have an aot-id of null. This way we will retain the old behavior of UNREGISTERED classes.
Right, so we can continue to maintain a single list in ArchiveBuilder::_klasses but at the time of writing in the preimage or final image, they would be stored in a map with aot-id as the key and list of classes loaded by that loader as the value.
Regarding duplicates, we want to allow duplicates in the UNREGISTERED category when custom loader support is enabled, but not when legacy cds archive is used. So I guess just disabling the duplicate processing when custom loader support is enabled should be sufficient to make it work.
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
I tested it with a single URLClassLoader delegating to system loader and it seemed to work fine.
yup, I will do that. I just wanted to get this out to get the feedback if this is in line with what we have been discussing. |
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
… any class Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
file Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
|
Per our meeting yesterday, here are some edge cases to build test cases for. Notation: A is one of the "big three" CLs (boot/sys/app class loaders) which the AOT cache already supports as "live objects". C, C2 are CCLs (custom CLs) that delegate directly to an A. Such delegation is written A←C. (Backwards arrow favors left-to-right order of lookup.) Q is a class loader that delegates to another CCL (not an A CL). To make classpaths explicit we write The case involving To support simultaneously extending the command-line classpath ( The simplest thing to do, for starters, might be to disable CCLs totally, if there is not an EXACT match for the command-line classpath. (No Similar points for multi-level delegation ( Our overall consensus yesterday was "start stupid". If in doubt, leave it out, for now. We are starting with limited capabilities in order to avoid edge cases we might not fully understand yet. As we gain confidence, we can relax restrictions. (It’s hard to go the other way.) (Ioi, feel free to correct me if I missed something.) |
|
I think John's rules make a lot of sense. Let's start with more restrictive support and make sure the implementation is solid. Then, we can gradually extend it to support more use cases. I think we can develop some dynamic checks to validate the current implementation. These checks will also be useful when we extend AOT support to other custom class loaders: (1) When the JVM is running in training mode, and a class loader is marked as AOT compatible (currently only URLClassLoaders with local files):
(2) When the JVM is running in production run, and a ClassLoader if found to be adaptable from the AOT cache:
I think the production run checks can validate the implementation of code generators that are supposed to generate predicable class shapes. This way, we can provide AOT support for of dynamic languages implemented on top of the JVM. (@headius what do you think?) |
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
Signed-off-by: Ashutosh Mehra <asmehra@redhat.com>
This PR is WIP
It allows user code to set the identity of the ClassLoader object. The identity is of type String. Each InstanceKlass also stores the id of the class loader that loaded the class.. During assembly phase VM stores a map of classloader id to the set of classes loaded by the class loader object in the aot cache. These classes are loaded and linked when the class loader object is created during the production run.
This patch also extends URLClassLoader to transparently set the identity when a new URLClassLoader instance is created, provided all its urls refer to jar files. Currently the id is same as the classpath (set of urls) of the URLClassLoader, but that need not be the case; id can be any function of the urls or their contents. During training run, VM stores a mapping of URLClassLoader id to classpath in the aot config file. This information is propagated to the final aot cache. During production run when the URLClassLoader instance is created, VM verifies that the runtime classpath of the URLClassLoader matches with the classpath stored in the aot cache. If the verification passes, then all the classes associated with the URLClassLoader id are loaded and linked.
Progress
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/leyden.git pull/109/head:pull/109$ git checkout pull/109Update a local copy of the PR:
$ git checkout pull/109$ git pull https://git.openjdk.org/leyden.git pull/109/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 109View PR using the GUI difftool:
$ git pr show -t 109Using diff file
Download this PR as a diff file:
https://git.openjdk.org/leyden/pull/109.diff