Skip to content

8231269: CompileTask::is_unloaded is slow due to JNIHandles type checks #24018

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

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
cc8345e
JNIHandles -> VM(Weak)
shipilev Mar 12, 2025
2978cb5
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev Mar 31, 2025
d965fef
Shared utility class for method unload blocking
shipilev Mar 31, 2025
669dc6a
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev Apr 23, 2025
b454cd4
Fully encapsulate Method*
shipilev Apr 23, 2025
bc4ab32
Renames
shipilev Apr 23, 2025
7f32b31
Touchups
shipilev Apr 23, 2025
2ec579c
Purge extra fluff
shipilev Apr 23, 2025
63650fa
Fix VMStructs
shipilev Apr 23, 2025
91d38ff
Allow UMH::_method access from VMStructs
shipilev Apr 23, 2025
6f26b73
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev Apr 24, 2025
e165f59
Inline guard
shipilev Apr 24, 2025
eb5e21d
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev Apr 25, 2025
4db23b3
Attempt at phasing doc
shipilev Apr 25, 2025
07a3cae
Do not accept nullptr methods
shipilev Apr 25, 2025
be3a3d6
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev Apr 28, 2025
eaf3f14
Simplify a bit
shipilev Apr 28, 2025
9f44cb5
Improve get_method_blocker
shipilev Apr 29, 2025
baea6cd
Move to oops
shipilev Apr 30, 2025
f53cf7a
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev May 8, 2025
ff5463a
Rework for safer concurrency
shipilev May 9, 2025
1cdbed2
Tracking UMH state more accurately
shipilev May 9, 2025
f60bf96
Fix build failures: add more headers
shipilev May 12, 2025
ce737c5
More thorough locking and redefinition escape hatch
shipilev May 12, 2025
f239c22
Fix release builds
shipilev May 12, 2025
33e545e
More touchups
shipilev May 12, 2025
a2f9955
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev May 13, 2025
7f4ed16
Simplify select_for_compilation
shipilev May 13, 2025
59798bd
Rename CompilerTask::is_unloaded back to avoid losing comment context
shipilev May 13, 2025
4d33a4d
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev May 15, 2025
43e5fca
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev May 21, 2025
51390bc
Spin lock induces false sharing
shipilev May 21, 2025
f6bbc8d
More touchups
shipilev May 21, 2025
22b6629
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev May 26, 2025
0c1c5d6
Switch to mutable
shipilev May 26, 2025
d5e482a
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev May 27, 2025
d5a8a27
Merge branch 'master' into JDK-8231269-compile-task-weaks
shipilev Jul 9, 2025
41dbb21
Deal with things without spinlocks
shipilev Jul 9, 2025
54733a6
Move release() to destructor
shipilev Jul 10, 2025
70a8b4b
Tune up for release builds
shipilev Jul 10, 2025
576a259
Further simplify the API
shipilev Jul 10, 2025
66bb4da
Use enum class
shipilev Jul 10, 2025
b27c063
Docs touchup
shipilev Jul 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/hotspot/share/ci/ciEnv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
#include "compiler/compilerEvent.hpp"
#include "compiler/compileTask.hpp"
#include "compiler/compileTask.inline.hpp"
#include "compiler/disassembler.hpp"
#include "gc/shared/collectedHeap.inline.hpp"
#include "interpreter/bytecodeStream.hpp"
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/ci/ciMethod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
#include "compiler/abstractCompiler.hpp"
#include "compiler/compilerDefinitions.inline.hpp"
#include "compiler/compilerOracle.hpp"
#include "compiler/compileTask.hpp"
#include "compiler/compileTask.inline.hpp"
#include "compiler/methodLiveness.hpp"
#include "interpreter/interpreter.hpp"
#include "interpreter/linkResolver.hpp"
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/compiler/compilationMemoryStatistic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#include "compiler/compilerDirectives.hpp"
#include "compiler/compilerOracle.hpp"
#include "compiler/compilerThread.hpp"
#include "compiler/compileTask.hpp"
#include "compiler/compileTask.inline.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/arena.hpp"
Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/compiler/compilationPolicy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "compiler/compileBroker.hpp"
#include "compiler/compilerDefinitions.inline.hpp"
#include "compiler/compilerOracle.hpp"
#include "compiler/compileTask.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/method.inline.hpp"
#include "oops/methodData.hpp"
Expand Down
4 changes: 3 additions & 1 deletion src/hotspot/share/compiler/compileBroker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "compiler/compileLog.hpp"
#include "compiler/compilerEvent.hpp"
#include "compiler/compilerOracle.hpp"
#include "compiler/compileTask.inline.hpp"
#include "compiler/directivesParser.hpp"
#include "gc/shared/memAllocator.hpp"
#include "interpreter/linkResolver.hpp"
Expand Down Expand Up @@ -1707,7 +1708,8 @@ void CompileBroker::wait_for_completion(CompileTask* task) {

JavaThread* thread = JavaThread::current();

methodHandle method(thread, task->method());
methodHandle method(thread, task->is_unloaded() ? nullptr : task->method());

bool free_task;
#if INCLUDE_JVMCI
AbstractCompiler* comp = compiler(task->comp_level());
Expand Down
58 changes: 25 additions & 33 deletions src/hotspot/share/compiler/compileTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
#include "compiler/compilerDirectives.hpp"
#include "compiler/compileTask.hpp"
#include "compiler/compileTask.inline.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/resourceArea.hpp"
#include "oops/klass.inline.hpp"
#include "oops/method.inline.hpp"
#include "oops/unloadableMethodHandle.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/mutexLocker.hpp"
Expand All @@ -44,11 +45,9 @@ CompileTask::CompileTask(int compile_id,
int comp_level,
int hot_count,
CompileReason compile_reason,
bool is_blocking) {
Thread* thread = Thread::current();
bool is_blocking) :
_method_handle(method()) {
_compile_id = compile_id;
_method = method();
_method_holder = JNIHandles::make_weak_global(Handle(thread, method->method_holder()->klass_holder()));
_osr_bci = osr_bci;
_is_blocking = is_blocking;
JVMCI_ONLY(_has_waiter = CompileBroker::compiler(comp_level)->is_jvmci();)
Expand Down Expand Up @@ -81,11 +80,6 @@ CompileTask::CompileTask(int compile_id,
}

CompileTask::~CompileTask() {
if (_method_holder != nullptr && JNIHandles::is_weak_global_handle(_method_holder)) {
JNIHandles::destroy_weak_global(_method_holder);
} else {
JNIHandles::destroy_global(_method_holder);
}
if (_failure_reason_on_C_heap && _failure_reason != nullptr) {
os::free((void*) _failure_reason);
_failure_reason = nullptr;
Expand All @@ -112,38 +106,36 @@ AbstractCompiler* CompileTask::compiler() const {
return CompileBroker::compiler(_comp_level);
}

// Replace weak handles by strong handles to avoid unloading during compilation.
CompileTask* CompileTask::select_for_compilation() {
if (is_unloaded()) {
// Guard against concurrent class unloading
return nullptr;
}
Thread* thread = Thread::current();
assert(_method->method_holder()->is_loader_alive(), "should be alive");
Handle method_holder(thread, _method->method_holder()->klass_holder());
JNIHandles::destroy_weak_global(_method_holder);
_method_holder = JNIHandles::make_global(method_holder);
return this;
}

void CompileTask::mark_on_stack() {
if (is_unloaded()) {
return;
if (_method_handle.is_safe()) {
_method_handle.make_always_safe();
return this;
}
// Mark these methods as something redefine classes cannot remove.
_method->set_on_stack(true);
return nullptr;
}

bool CompileTask::is_unloaded() const {
return _method_holder != nullptr && JNIHandles::is_weak_global_handle(_method_holder) && JNIHandles::is_weak_global_cleared(_method_holder);
return !_method_handle.is_safe();
}

// ------------------------------------------------------------------
// RedefineClasses support

void CompileTask::mark_on_stack() {
// Mark these methods as something redefine classes cannot remove.
// Redefinition runs in VM thread, which cannot ask about the method
// safety. This is why we end up asking for method unsafely.
assert_at_safepoint();
assert(Thread::current()->is_VM_thread(), "Sanity");
_method_handle.method_unsafe()->set_on_stack(true);
}

void CompileTask::metadata_do(MetadataClosure* f) {
if (is_unloaded()) {
return;
}
f->do_metadata(method());
// Redefinition runs in VM thread, which cannot ask about the method
// safety. This is why we end up asking for method unsafely.
assert_at_safepoint();
assert(Thread::current()->is_VM_thread(), "Sanity");
f->do_metadata(_method_handle.method_unsafe());
}

// ------------------------------------------------------------------
Expand Down
7 changes: 4 additions & 3 deletions src/hotspot/share/compiler/compileTask.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "code/nmethod.hpp"
#include "compiler/compileLog.hpp"
#include "memory/allocation.hpp"
#include "oops/unloadableMethodHandle.hpp"
#include "runtime/mutexLocker.hpp"
#include "utilities/xmlstream.hpp"

Expand Down Expand Up @@ -84,8 +85,7 @@ class CompileTask : public CHeapObj<mtCompiler> {
private:
static int _active_tasks;
int _compile_id;
Method* _method;
jobject _method_holder;
UnloadableMethodHandle _method_handle;
int _osr_bci;
bool _is_complete;
bool _is_success;
Expand Down Expand Up @@ -120,8 +120,9 @@ class CompileTask : public CHeapObj<mtCompiler> {
~CompileTask();
static void wait_for_no_active_tasks();

inline Method* method() const;

int compile_id() const { return _compile_id; }
Method* method() const { return _method; }
int osr_bci() const { return _osr_bci; }
bool is_complete() const { return _is_complete; }
bool is_blocking() const { return _is_blocking; }
Expand Down
36 changes: 36 additions & 0 deletions src/hotspot/share/compiler/compileTask.inline.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#ifndef SHARE_COMPILER_COMPILETASK_INLINE_HPP
#define SHARE_COMPILER_COMPILETASK_INLINE_HPP

#include "compiler/compileTask.hpp"

#include "oops/unloadableMethodHandle.inline.hpp"

inline Method* CompileTask::method() const {
return _method_handle.method();
}

#endif // SHARE_COMPILER_COMPILETASK_INLINE_HPP
2 changes: 1 addition & 1 deletion src/hotspot/share/jvmci/jvmciEnv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include "classfile/systemDictionary.hpp"
#include "code/codeCache.hpp"
#include "compiler/compilerOracle.hpp"
#include "compiler/compileTask.hpp"
#include "compiler/compileTask.inline.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetNMethod.hpp"
#include "jvm_io.h"
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/oops/trainingData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "compiler/compileTask.hpp"
#include "compiler/compileTask.inline.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/metaspaceClosure.hpp"
#include "memory/resourceArea.hpp"
Expand Down
102 changes: 102 additions & 0 deletions src/hotspot/share/oops/unloadableMethodHandle.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#ifndef SHARE_OOPS_UNLOADABLE_METHOD_HANDLE_HPP
#define SHARE_OOPS_UNLOADABLE_METHOD_HANDLE_HPP

#include "memory/padded.hpp"
#include "oops/oopHandle.hpp"
#include "oops/weakHandle.hpp"

// Unloadable method handle.
//
// This handle allows holding to Method* safely without delaying class unloading
// of its holder.
//
// This handle can be in 2 states:
// 1. Unsafe (weak). Method* is present, but its holder is only weakly-reachable, and can
// be unloaded. Users need to check is_safe() before calling method().
// method() is safe to call iff we have not crossed a safepoint since construction
// or last is_safe() check. Calling make_always_safe() after is_safe() check
// moves handle to the strong state.
// 2. Safe (strong). Method* holder is strongly reachable, cannot be unloaded.
// Calling method() is always safe in this state.
//
// The handle transitions are one-shot:
// unsafe (weak) --(make_always_safe) --> safe (strong)
//
// There are internal shortcuts that bypass this mechanics when handle knows
// the method holder is permanent and would not be unloaded. This is an implementation
// detail, it does not change any external contract. Using this handle for permanent
// method holders provides future safety.
//
// Common usage pattern:
//
// UnloadableMethodHandle mh(method); // Now in unsafe (weak) state.
// mh.method()->print_on(tty); // method() is good until the next safepoint.
// <safepoint>
// if (!mh.is_safe()) { // Safe to use method()?
// return; // Nope!
// }
// mh.method()->print_on(tty); // method() is good until the next safepoint.
// mh.make_always_safe(); // Now in safe (strong) state.
// <safepoint>
// mh.method()->print_on(tty); // method() is always safe now.
//

class Method;

class UnloadableMethodHandle {
friend class VMStructs;
private:
enum class State {
PERMANENT,
WEAK,
STRONG,
RELEASED,
};

State volatile _state;

Method* _method;
WeakHandle _weak_handle;
OopHandle _strong_handle;

inline State get_state() const;
inline void set_state(State to);
inline bool transit_state(State from, State to);
inline oop get_unload_blocker(Method* method);

public:
UnloadableMethodHandle(Method* method);
~UnloadableMethodHandle();

inline Method* method() const;
inline Method* method_unsafe() const;

inline bool is_safe() const;
void make_always_safe();
};

#endif // SHARE_OOPS_UNLOADABLE_METHOD_HANDLE_HPP
Loading