-
Notifications
You must be signed in to change notification settings - Fork 5.8k
8354897: Support Soft/Weak Reference in AOT cache #24757
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8354897: Support Soft/Weak Reference in AOT cache #24757
Conversation
👋 Welcome back iklam! A progress list of the required criteria for merging this PR into |
@iklam This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 21 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. ➡️ To integrate this PR with the above commit message to the |
@iklam |
@iklam |
@iklam |
@iklam |
Webrevs
|
Object referent = key.get(); | ||
if (referent == null) { | ||
// We don't need this key anymore. Add to stale queue | ||
((Reference)key).enqueue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is enqueue necessary here? Afaik this map only uses the queue to be alert of member reference being garbage collected and then remove stale elements. Since at this stage this map is no longer used, maybe key.unused()
is sufficient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you are proposing. But enqueue is used to ensure that inactive references are pruned from the data structure so that dumping only includes active references. This way, the JVM that loads these objects create these references as active and hence follow the usual life cycle that every other reference does. I want to avoid having a new "special" life cycles for dumped references.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The map is still being used (and will be stored into the AOT cache). The key no longer has a referent, so we need to remove the key from the map. Adding the key to the stale queue and calling removeStaleReferences()
will accomplish this. key.unused()
does not remove the key from the map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I am fine with the rest of the core library changes.
src/hotspot/share/cds/heapShared.cpp
Outdated
@@ -1401,12 +1405,17 @@ class HeapShared::ReferentPusher: public BasicOopIterateClosure { | |||
template <class T> void do_oop_work(T *p) { | |||
oop obj = RawAccess<>::oop_load(p); | |||
if (!CompressedOops::is_null(obj)) { | |||
size_t field_delta = pointer_delta(p, _referencing_obj, sizeof(char)); | |||
int field_offset = pointer_delta_as_int((char*)p, cast_from_oop<char*>(_referencing_obj)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The RawAccess oop load above should probably be a HeapAccess<ON_UNKNOWN_OOP_REF>::oop_load_at. By using the unknown oop ref and supplying the field offset, we allow GCs to infer the actual reference strength in the backend when loading and apply appropriate barriers for non-strong references, while also applying appropriate load barriers for conc GCs.
private static native int getCDSConfigStatus(); | ||
private static native void logLambdaFormInvoker(String line); | ||
|
||
|
||
private static ArrayList<Object> keepAliveList; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth adding a comment stating this list should only be populated during an assembly phase? That it should be null in both training and production runs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added comments and asserts for clarification.
// Reference handling is complex. In this version, we implement only enough functionality to support | ||
// the use of Weak/Soft references used by java.lang.invoke. | ||
// | ||
// We intent to evolve the implementation in the future by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// We intent to evolve the implementation in the future by | |
// We intend to evolve the implementation in the future by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
…DanHeidinga comment -- renamed to MethodType::assemblySetup()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than reading the referent with the wrong accessor in the assert, I think this looks very good. Thanks for sorting this out in a clean and robust way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The core library changes look good.
…r AOTReferenceObjSupport::is_enabled()
InstanceKlass* ik = InstanceKlass::cast(k); | ||
ik->initialize(CHECK); | ||
|
||
TempNewSymbol field_name = SymbolTable::new_symbol("N""ULL"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why "N""ULL", not "NULL"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's because a stand-lone word "NULL" will cause hotspot/jtreg/sources/TestNoNULL.java to fail. I have renamed the internal field to NULL_QUEUE to avoid the failure, per recommendation of @kimbarrett
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the explanation!
… failing hotspot/jtreg/sources/TestNoNULL.java
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm. Just a few comments.
if (ref2.get() == null) { | ||
throw new RuntimeException("ref2.get() should not be null"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The referent of ref2 is never strong. If a GC occurs between makeRef2() and this check, we could hit this Exception. The test should either keep it strongly reachable until after this check or just not test wether it is reachable before the call to System.gc().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the check that was before the GC.
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@@ -73,6 +74,7 @@ void AOTArtifactFinder::find_artifacts() { | |||
// Note, if a class is not excluded, it does NOT mean it will be automatically included | |||
// into the AOT cache -- that will be decided by the code below. | |||
SystemDictionaryShared::finish_exclusion_checks(); | |||
AOTReferenceObjSupport::init_keep_alive_objs_table(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be guarded with INCLUDE_CDS_JAVA_HEAP
somehow. I guess the cleanest way would be to use NOT_CDS_JAVA_HEAP_RETURN
in aotReferenceObjSupport.hpp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added NOT_CDS_JAVA_HEAP_RETURN
to the declaration of init_keep_alive_objs_table
. All the other functions are called only inside INCLUDE_CDS_JAVA_HEAP
blocks, so I didn't change them to avoid cluttering the code.
runtimeSetup(); | ||
} | ||
|
||
private static void runtimeSetup() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if you are planning an annotation or something to mark these "runtimeSetup" methods but each one will minimally need a comment so that it's clear to anyone touching this code that it may be called by the VM when loading the AOT cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the plan is to use annotation to mark the runtimeSetup
and assemblySetup
methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a comment above the new runtimeSetup()
method in Reference.java. For the other existing occurrences, I plan to address them in a separate RFE, probably JDK-8342481
|
||
for (ReferenceKey<K> key : map.keySet()) { | ||
Object referent = key.get(); | ||
if (referent == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be key.refersTo(null)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is immediately passed to CDS.keepAlive
, two separate calls of refersTo
and get
may produce null
on the second call, I presume.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I missed that referent
was used later if non-null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the comment addition requested by Alan.
Thanks everyone for the review. Passed tiers 1-4, as well as the |
Going to push as commit 1ff7e81.
Your commit was automatically rebased without conflicts. |
// Called from JVM when loading an AOT cache | ||
static { | ||
runtimeSetup(); | ||
} | ||
|
||
private static void runtimeSetup() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment was added in the wrong spot:
// Called from JVM when loading an AOT cache | |
static { | |
runtimeSetup(); | |
} | |
private static void runtimeSetup() { | |
static { | |
runtimeSetup(); | |
} | |
// Called from JVM when loading an AOT cache | |
private static void runtimeSetup() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I will fix in an related PR #24979
This PR contains 2 parts
AOTReferenceObjSupport
and new Java methodReferencedKeyMap::prepareForAOTCache()
developed by @iklam on the advice of @fisk from the GC team. These control the lifecycles of reference objects during the assembly phase to simplify the implementation.One problem we faced in this PR is the handling of Reference objects that are waiting for clean up. Currently, the only cached Reference objects that require clean up are the
WeakReferenceKey
s used byReferencedKeyMap
(which is used byMethodType::internTable
):WeakReferenceKey
K has been collected, the key will be placed onUniverse::reference_pending_list()
. It's linked to other pending references with theReference::discovered
field. At this point, K is still stored in theReferencedKeyMap
.ReferencedKeyMap
, it will discover K, and it may also discover other pending references that are not intended for the AOT cache. As a result, we end up caching unnecessary objects.ReferencedKeyMap::prepareForAOTCache()
avoids the above problem. It goes over all entries in the table:Therefore, by the time heapShared.cpp starts scanning the
ReferencedKeyMap
, it will never see any keys that are on the pending list, so we will not see unintended objects.This implementation is the very first step of Reference support in the AOT cache, so we chose a simplified approach that makes no assumptions on when the pending reference list is processed. This is sufficient for the current set of references objects in the AOT cache.
In the future, we may relax the implementation to allow for other use cases.
Progress
Issue
Reviewers
Contributors
<[email protected]>
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/24757/head:pull/24757
$ git checkout pull/24757
Update a local copy of the PR:
$ git checkout pull/24757
$ git pull https://git.openjdk.org/jdk.git pull/24757/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 24757
View PR using the GUI difftool:
$ git pr show -t 24757
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/24757.diff
Using Webrev
Link to Webrev Comment