From e704c055a4cf2aab77cc2b3d034f5a8b8d9e3331 Mon Sep 17 00:00:00 2001 From: Oli Gillespie Date: Wed, 9 Oct 2024 15:28:44 +0000 Subject: [PATCH] 8340547: Starting many threads can delay safepoints Reviewed-by: shade, qamai, dholmes --- src/hotspot/share/prims/jvm.cpp | 3 ++- src/hotspot/share/runtime/globals.hpp | 4 ++++ src/hotspot/share/runtime/mutexLocker.cpp | 3 +++ src/hotspot/share/runtime/mutexLocker.hpp | 2 ++ src/hotspot/share/runtime/threads.cpp | 6 ++++-- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index a246c2c50d6..5d5d8aa7df9 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2948,9 +2948,10 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) // We must release the Threads_lock before we can post a jvmti event // in Thread::start. { + ConditionalMutexLocker throttle_ml(ThreadsLockThrottle_lock, UseThreadsLockThrottleLock); // Ensure that the C++ Thread and OSThread structures aren't freed before // we operate. - MutexLocker mu(Threads_lock); + MutexLocker ml(Threads_lock); // Since JDK 5 the java.lang.Thread threadStatus is used to prevent // re-starting an already started thread, so we should usually find diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 2bcc26043cd..b568e769304 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1991,6 +1991,10 @@ const int ObjectAlignmentInBytes = 8; \ product(bool, StressSecondarySupers, false, DIAGNOSTIC, \ "Use a terrible hash function in order to generate many collisions.") \ + \ + product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC, \ + "Use an extra lock during Thread start and exit to alleviate" \ + "contention on Threads_lock.") \ // end of RUNTIME_FLAGS diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index a0ba783c364..769c7695192 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -66,6 +66,7 @@ Monitor* CodeCache_lock = nullptr; Mutex* TouchedMethodLog_lock = nullptr; Mutex* RetData_lock = nullptr; Monitor* VMOperation_lock = nullptr; +Monitor* ThreadsLockThrottle_lock = nullptr; Monitor* Threads_lock = nullptr; Mutex* NonJavaThreadsList_lock = nullptr; Mutex* NonJavaThreadsListSync_lock = nullptr; @@ -317,6 +318,8 @@ void mutex_init() { MUTEX_DEFN(JVMCIRuntime_lock , PaddedMonitor, safepoint, true); #endif + MUTEX_DEFN(ThreadsLockThrottle_lock , PaddedMonitor, safepoint); + // These locks have relative rankings, and inherit safepoint checking attributes from that rank. MUTEX_DEFL(VtableStubs_lock , PaddedMutex , CompiledIC_lock); // Also holds DumpTimeTable_lock MUTEX_DEFL(CodeCache_lock , PaddedMonitor, VtableStubs_lock); diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 3da85958501..98cb27d0b81 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -61,6 +61,8 @@ extern Monitor* CodeCache_lock; // a lock on the CodeCache extern Mutex* TouchedMethodLog_lock; // a lock on allocation of LogExecutedMethods info extern Mutex* RetData_lock; // a lock on installation of RetData inside method data extern Monitor* VMOperation_lock; // a lock on queue of vm_operations waiting to execute +extern Monitor* ThreadsLockThrottle_lock; // used by Thread start/exit to reduce competition for Threads_lock, + // so a VM thread calling a safepoint is prioritized extern Monitor* Threads_lock; // a lock on the Threads table of active Java threads // (also used by Safepoints too to block threads creation/destruction) extern Mutex* NonJavaThreadsList_lock; // a lock on the NonJavaThreads list diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 8266bd86a96..45aaa769856 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -1028,7 +1028,9 @@ void Threads::add(JavaThread* p, bool force_daemon) { void Threads::remove(JavaThread* p, bool is_daemon) { // Extra scope needed for Thread_lock, so we can check // that we do not remove thread without safepoint code notice - { MonitorLocker ml(Threads_lock); + { + ConditionalMutexLocker throttle_ml(ThreadsLockThrottle_lock, UseThreadsLockThrottleLock); + MonitorLocker ml(Threads_lock); if (ThreadIdTable::is_initialized()) { // This cleanup must be done before the current thread's GC barrier @@ -1076,7 +1078,7 @@ void Threads::remove(JavaThread* p, bool is_daemon) { // Notify threads waiting in EscapeBarriers EscapeBarrier::thread_removed(p); - } // unlock Threads_lock + } // unlock Threads_lock and ThreadsLockThrottle_lock // Reduce the ObjectMonitor ceiling for the exiting thread. ObjectSynchronizer::dec_in_use_list_ceiling();