RFR: 8231269: CompileTask::is_unloaded is slow due to JNIHandles type checks [v11]
Coleen Phillimore
coleenp at openjdk.org
Wed May 7 20:33:56 UTC 2025
On Wed, 30 Apr 2025 07:23:39 GMT, Aleksey Shipilev <shade at openjdk.org> wrote:
>> [JDK-8163511](https://bugs.openjdk.org/browse/JDK-8163511) made the `CompileTask` improvement to avoid blocking class unloading if a relevant compile task is in queue. Current code does a sleight-of-hand to make sure the the `method*` in `CompileTask` are still valid before using them. Still a noble goal, so we keep trying to do this.
>>
>> The code tries to switch weak JNI handle with a strong one when it wants to capture the holder to block unloading. Since we are reusing the same field, we have to do type checks like `JNIHandles::is_weak_global_handle(_method_holder)`. Unfortunately, that type-check goes all the way to `OopStorage` allocation code to verify the handle is really allocated in the relevant `OopStorage`. This takes internal `OopStorage` locks, and thus is slow.
>>
>> This issue is clearly visible in Leyden, when there are lots of `CompileTask`-s in the queue, dumped by AOT code loader. It also does not help that `CompileTask::select_task` is effectively quadratic in number of methods in queue, so we end up calling `CompileTask::is_unloaded` very often.
>>
>> It is possible to mitigate this issue by splitting the related fields into weak and strong ones. But as Kim mentions in the bug, we should not be using JNI handles here at all, and instead go directly for relevant `OopStorage`-s. This is what this PR does, among other things that should hopefully make the whole mechanics clearer.
>>
>> Additional testing:
>> - [x] Linux x86_64 server fastdebug, `compiler/classUnloading`, 100x still passes; these tests are sensitive to bugs in this code
>> - [x] Linux x86_64 server fastdebug, `all`
>> - [x] Linux AArch64 server fastdebug, `all`
>
> Aleksey Shipilev has updated the pull request incrementally with one additional commit since the last revision:
>
> Move to oops
This is a cleaner way to do this. I believe it's what we discussed with Kim. He can confirm. Some questions and comments and a small nit.
src/hotspot/share/compiler/compileBroker.cpp line 1697:
> 1695: JavaThread* thread = JavaThread::current();
> 1696:
> 1697: methodHandle method(thread, task->method());
I think this is safe because the Method* is in the CompileTask and redefinition will find it there. Being unsure of this is why this is here in a handle.
src/hotspot/share/oops/unloadableMethodHandle.inline.hpp line 35:
> 33: #include "oops/weakHandle.inline.hpp"
> 34:
> 35: inline UnloadableMethodHandle::UnloadableMethodHandle(Method* method) {
This should initialize method in the ctor initializer list.
src/hotspot/share/oops/unloadableMethodHandle.inline.hpp line 51:
> 49: // Method holder class cannot be unloaded.
> 50: return nullptr;
> 51: }
This is nice that this doesn't require creating a jni handle for unloadable class loaders with this change.
src/hotspot/share/runtime/vmStructs.cpp line 1266:
> 1264: declare_toplevel_type(CDSFileMapRegion) \
> 1265: declare_toplevel_type(UpcallStub::FrameData) \
> 1266: declare_toplevel_type(UnloadableMethodHandle) \
So are these left for the async profiler?
-------------
Marked as reviewed by coleenp (Reviewer).
PR Review: https://git.openjdk.org/jdk/pull/24018#pullrequestreview-2823027214
PR Review Comment: https://git.openjdk.org/jdk/pull/24018#discussion_r2078430169
PR Review Comment: https://git.openjdk.org/jdk/pull/24018#discussion_r2078443576
PR Review Comment: https://git.openjdk.org/jdk/pull/24018#discussion_r2078379288
PR Review Comment: https://git.openjdk.org/jdk/pull/24018#discussion_r2078446115
More information about the hotspot-gc-dev
mailing list