RFR: 8317269: Store old classes in linked state in AOT cache [v5]
Coleen Phillimore
coleenp at openjdk.org
Fri Aug 29 21:54:47 UTC 2025
On Fri, 29 Aug 2025 20:50:31 GMT, Ioi Lam <iklam at openjdk.org> wrote:
>> During the assembly phase of the AOT cache, we link and verify all classes that were loaded during the training run. When verifying a class like this:
>>
>>
>> class X {
>> A getA() { return new B(); } // Verifier requires B to be a subtype of A.
>> }
>>
>>
>> We remember `A` and `B` as the "verification dependencies" of `X`. A class will be excluded from the AOT cache if any of its verification dependencies are excluded. For example, `X` will be excluded if
>>
>> - `A` fails verification
>> - `B` is a signed class, which is always excluded from the AOT cache
>>
>> Conversely, if both `A` and `B` are included in the AOT cache, in the production run, they will be unconditionally loaded during VM bootstrap. Therefore, we can guarantee that the verification result computed for `X` will remain valid during the production run.
>>
>> Notes for reviewers:
>>
>> - The checks for verification dependencies are done inside `SystemDictionaryShared::check_exclusion_for_self_and_dependencies()`. These checks are done for both old and new classes. Since the dependencies can form a cyclic graph, the checks cannot be implemented with a simple recursion. See "Algorithm notes" in this function for details.
>> - The verification dependencies for "new" classes are already stored in `DumpTimeClassInfo::_verifier_constraints`.
>> - This PR adds code to record the verification dependencies for "old" classes into `DumpTimeClassInfo::_old_verifier_dependencies`, by intercepting `JVM_FindClassFromCaller()`.
>> - This new functionality (store old classes in linked state) is available only when using the AOT cache. For simplicity, this functionality is not available with the old CDS workflows. See comments in `CDSConfig::is_preserving_verification_dependencies()`.
>
> Ioi Lam has updated the pull request with a new target base due to a merge or a rebase. The incremental webrev excludes the unrelated changes brought in by the merge/rebase. The pull request contains nine additional commits since the last revision:
>
> - Merge branch 'master' into 8317269-store-old-classes-in-linked-state-in-aot-cache
> - @matias9927 comments: fixed typos
> - clean up; removed unrelated change
> - Cleaned up iterate_verification_dependency_names so a return value of false from <func> means early termination (similar to HashTable::iterate())
> - Merge branch 'master' into 8317269-store-old-classes-in-linked-state-in-aot-cache
> - @coleenp comments
> - More bug fixes from JCK testing
> - Fixed bugs found in JCK testing
> - 8317269: Store old classes in linked state in AOT cache
I'm making progress. This is a large somewhat complicated change and I have some questions and comments.
src/hotspot/share/classfile/systemDictionaryShared.cpp line 469:
> 467:
> 468: // Returns true if the DumpTimeClassInfo::is_excluded() is true for at least one of k's exclusion dependencies.
> 469: bool SystemDictionaryShared::check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
So this checks one level of InstanceKlass* k's dependencies, and the table above collects one level of k or k's subclass's dependencies and calls this for those.
class K : extends B implements I, J verifies C {} - the above hashtable has B, I and J and C.
This function takes B - so checks B class.
class B : extends A implements intf verifies C again, why not {}
So it'd check A and intf and C again. But when do we check A's super?
It seems like the hashtable should be created for all levels of the hierarchy, so only one bit of code should walk the hierarchy, not two.
src/hotspot/share/classfile/systemDictionaryShared.cpp line 542:
> 540: }
> 541:
> 542: Klass* SystemDictionaryShared::find_verification_dependency_bottom_class(InstanceKlass* k, Symbol* dependency_class_name) {
Why don't you have this return InstanceKlasss so that the callers don't have to check the same thing. What you want is the bottom InstanceKlass so for [Lfoo; - you want foo. Since class foo is the thing you care about. Maybe the comment about why you return nullptr from check_verification_dependency_exclusion() could be here instead.
These names are a bit repetative, can you just call this find_bottom_instance_klass() and maybe it could be used for other things too.
src/hotspot/share/classfile/systemDictionaryShared.cpp line 1036:
> 1034: DumpTimeClassInfo* info = get_info(ik);
> 1035: Handle loader(current, ik->class_loader());
> 1036: Klass* k = SystemDictionary::find_instance_or_array_klass(current, name, loader);
The caller just did this, didn't it? So, you should just be able to pass in from_class (unless it's null).
-------------
PR Review: https://git.openjdk.org/jdk/pull/26754#pullrequestreview-3169748899
PR Review Comment: https://git.openjdk.org/jdk/pull/26754#discussion_r2311370713
PR Review Comment: https://git.openjdk.org/jdk/pull/26754#discussion_r2311313095
PR Review Comment: https://git.openjdk.org/jdk/pull/26754#discussion_r2311248609
More information about the hotspot-dev
mailing list