Track loaded classes with valid class chains - #20516
Conversation
| * must have identical chains). The ROM classes of these classes must have been | ||
| * shared when they were loaded in order to be tracked. |
There was a problem hiding this comment.
It isn't necessary for AOT dependency tracking to track classes whose ROM classes weren't in the SCC when they were loaded - if they weren't, then the only way compiled code in the SCC could reference them is if this JVM was responsible for compiling that code (and remembering the class during the JVM run). If that's the case, we don't need to track the dependencies of that code anyway.
I could have the table include all classes by updating the table in TR_J9SharedCache::rememberClass(), but there was one potential race that I was concerned about, where we remember a class that's loaded but not initialized at the same time as the class initialization hook is triggered for that class. We might end up not notifying the table of the initialization properly in that case. I mention this because if the _offsetMap here is used to look up candidate classes during relocation in case looking them up in a class loader fails, then we might want freshly-remembered classes in the table to improve that fallback. That's an optimization, though.
There was a problem hiding this comment.
I meant to say class chains, not ROM classes above. I could also rememberClass() on class load instead of using classMatchesCachedVersion(), or track every shared class. I believe either would also work.
|
Attn @mpirvu. |
|
Since you added new files, they need to be added the makefile system. |
0e6b11c to
1e66b1f
Compare
mpirvu
left a comment
There was a problem hiding this comment.
Overall it looks good. I only have some small comments/suggestions.
Have you considered what happens if we run out of memory during the allocations of the new data structures?
| if (!_sharedCache->isClassInSharedCache(freshClass, &freshClassOffset) && !_sharedCache->isClassInSharedCache(oldClass, &oldClassOffset)) | ||
| return; | ||
|
|
||
| if (oldClassOffset != freshClassOffset) |
There was a problem hiding this comment.
For class redefinition, do both the oldClass and the fresh class go through the class loading process?
Also, doesn't the oldClass need to be eliminated from the OffsetEntry associated with its romClass?
There was a problem hiding this comment.
Yes, it would be cleaner if redefined class invalidation went through classLoadEvent (or some common method). The code in invalidateRedefinedClass should currently implement the same process that classLoadEvent does, because class initialization is only relevant for updating method dependencies.
I do classList.push_front(classInfo) right after collecting the subclasses of the old class, so the loop just below this should end up removing oldClass from its OffsetEntry.
|
I have thought about failure to allocate a bit, but I haven't addressed it in the code. I should. If we have a failure to allocate, then the |
I agree. If there is little memory in the system, performance tweaks are the last thing we need to worry about. |
dsouzai
left a comment
There was a problem hiding this comment.
I had some questions below
| // In a class redefinition event, an old class is replaced by a fresh class. If | ||
| // the ROM class offset changed as a result, it and all its subclasses that | ||
| // formerly had valid chains will now be guaranteed not to match, so the entries | ||
| // for these must be removed. If the new offset is valid, any class that didn't | ||
| // have an entry should be rechecked. |
There was a problem hiding this comment.
This comment says that if the ROM class offset changes, the entries must be removed. However, further down you have
if (oldClassOffset != freshClassOffset)
return;
which means that if the offset changed, you don't do anything, which contradicts the comment.
There was a problem hiding this comment.
Also, if the offset is different, then you definitely need to update the table. Otherwise, the table will have an entry with the oldClassOffset as the key but the freshClass as the value (because the freshClass is redefined in place). You may not need to recheck the subclass though.
There was a problem hiding this comment.
Typo introduced when I rewrote a bit of this. It should be ==. If the offsets are the same then the validation status remains unchanged.
| return NULL; | ||
|
|
||
| auto c_it = it->second._loadedClasses.begin(); | ||
| if (c_it == it->second._loadedClasses.end()) |
There was a problem hiding this comment.
Is it not possible to do if (it->second._loadedClasses.empty()) here?
There was a problem hiding this comment.
It is possible, but I'll be changing this so I iterate over all the loaded classes anyway, in case the first ones found are not initialized.
|
I think that |
345cd52 to
f5fdde4
Compare
Yes, they should be there. I could add a replacement for |
f5fdde4 to
3edef9e
Compare
| if (!_sharedCache->isClassInSharedCache(freshClass, &freshClassOffset) && !_sharedCache->isClassInSharedCache(oldClass, &oldClassOffset)) | ||
| return; | ||
|
|
||
| if (oldClassOffset == freshClassOffset) |
There was a problem hiding this comment.
If the offsets are the same, do you need to add oldClass into the table? I guess if something needs the class chain validation status, it will use the new class, but is there anything that might still use the old class to look up the status? Even if something does, does it matter if it can't find it? I'm gonna guess the answer is no, but I figure it's worth pointing out just in case.
There was a problem hiding this comment.
The oldClass would have been tracked already in classLoadEvent. I actually need to invalidate the oldClass and then register that freshClass was loaded. Should be fixed.
a6b88f6 to
2f4d248
Compare
| * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 | ||
| *******************************************************************************/ | ||
|
|
||
| #if !defined(PERSISTENT_COLLECTIONS_UNSUPPORTED) |
There was a problem hiding this comment.
Might be cleaner to do what is done for JITServer / CRIU, eg https://github.com/eclipse-openj9/openj9/blob/master/runtime/compiler/control/CMakeLists.txt#L36-L48
There was a problem hiding this comment.
Although I suppose the tradeoff is that you'd have to guard all places that include the header appropriately as well.
There was a problem hiding this comment.
I just pushed to include DependencyTable.cpp conditionally in the CMakeLists.txt, but I don't think the way I did it works. I think I'd need to define a feature flag so that DependencyTable.cpp is conditionally included in common.mk.ftl as well.
There was a problem hiding this comment.
I've reverted that.
c6a92d7 to
c74edbb
Compare
|
I think that should be it. I've added |
c74edbb to
27d681e
Compare
|
jenkins compile all JDK8 |
|
AIX compile error: |
27d681e to
d6a448e
Compare
|
The explicit constructor definition can just be removed. |
|
jenkins compile all JDK8 |
The new TR_AOTDependencyTable tracks all loaded classes whose class chains match what is stored in the local SCC. In future, it will also be responsible for tracking potential AOT loads; it will use this offset map to detect when all the dependencies of the stored AOT compilation have been satisfied and reduce the count of the associated method to induce a load in response. This class tracking is disabled by default. It can be controlled with the option -XX:[+|-]TrackAOTDependencies. Signed-off-by: Christian Despres <despresc@ibm.com>
d6a448e to
7b91b13
Compare
|
Latest failure was due to the fact that the start of and since the include for |
|
jenkins compile aix JDK8 |
|
jenkins compile all JDK8 |
|
jenkins test sanity all jdk21 |
The new TR_AOTDependencyTable tracks all loaded classes whose class chains match what is stored in the local SCC. In future, it will also be responsible for tracking potential AOT loads; it will use this offset map to detect when all the dependencies of the stored AOT compilation have been satisfied and reduce the count of the associated method to induce a load in response.
This class tracking is disabled by default. It can be controlled with the option -XX:[+|-]TrackAOTDependencies.
Related: #20529