mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
8298469: Obsolete legacy parallel class loading workaround for non-parallel-capable class loaders
Reviewed-by: dholmes, fparain
This commit is contained in:
parent
02a4ee206a
commit
932be3542d
@ -132,9 +132,6 @@ void PlaceholderEntry::add_seen_thread(JavaThread* thread, PlaceholderTable::cla
|
||||
SeenThread* threadEntry = new SeenThread(thread);
|
||||
SeenThread* seen = actionToQueue(action);
|
||||
|
||||
assert(action != PlaceholderTable::LOAD_INSTANCE || !EnableWaitForParallelLoad || seen == nullptr,
|
||||
"Only one LOAD_INSTANCE allowed at a time");
|
||||
|
||||
if (seen == nullptr) {
|
||||
set_threadQ(threadEntry, action);
|
||||
return;
|
||||
|
||||
@ -489,50 +489,6 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
|
||||
return superk;
|
||||
}
|
||||
|
||||
// We only get here if this thread finds that another thread
|
||||
// has already claimed the placeholder token for the current operation,
|
||||
// but that other thread either never owned or gave up the
|
||||
// object lock
|
||||
// Waits on SystemDictionary_lock to indicate placeholder table updated
|
||||
// On return, caller must recheck placeholder table state
|
||||
//
|
||||
// We only get here if
|
||||
// 1) custom classLoader, i.e. not bootstrap classloader
|
||||
// 2) custom classLoader has broken the class loader objectLock
|
||||
// so another thread got here in parallel
|
||||
//
|
||||
// lockObject must be held.
|
||||
// Complicated dance due to lock ordering:
|
||||
// Must first release the classloader object lock to
|
||||
// allow initial definer to complete the class definition
|
||||
// and to avoid deadlock
|
||||
// Reclaim classloader lock object with same original recursion count
|
||||
// Must release SystemDictionary_lock after notify, since
|
||||
// class loader lock must be claimed before SystemDictionary_lock
|
||||
// to prevent deadlocks
|
||||
//
|
||||
// The notify allows applications that did an untimed wait() on
|
||||
// the classloader object lock to not hang.
|
||||
static void double_lock_wait(JavaThread* thread, Handle lockObject) {
|
||||
assert_lock_strong(SystemDictionary_lock);
|
||||
|
||||
assert(EnableWaitForParallelLoad,
|
||||
"Only called when enabling legacy parallel class loading logic "
|
||||
"for non-parallel capable class loaders");
|
||||
assert(lockObject() != nullptr, "lockObject must be non-null");
|
||||
bool calledholdinglock
|
||||
= ObjectSynchronizer::current_thread_holds_lock(thread, lockObject);
|
||||
assert(calledholdinglock, "must hold lock for notify");
|
||||
assert(!is_parallelCapable(lockObject), "lockObject must not be parallelCapable");
|
||||
// These don't throw exceptions.
|
||||
ObjectSynchronizer::notifyall(lockObject, thread);
|
||||
intx recursions = ObjectSynchronizer::complete_exit(lockObject, thread);
|
||||
SystemDictionary_lock->wait();
|
||||
SystemDictionary_lock->unlock();
|
||||
ObjectSynchronizer::reenter(lockObject, recursions, thread);
|
||||
SystemDictionary_lock->lock();
|
||||
}
|
||||
|
||||
// If the class in is in the placeholder table, class loading is in progress.
|
||||
// For cases where the application changes threads to load classes, it
|
||||
// is critical to ClassCircularity detection that we try loading
|
||||
@ -554,20 +510,17 @@ static void handle_parallel_super_load(Symbol* name,
|
||||
}
|
||||
|
||||
// Bootstrap and non-parallel capable class loaders use the LOAD_INSTANCE placeholder to
|
||||
// wait for parallel class loading and to check for circularity error for Xcomp when loading
|
||||
// signature classes.
|
||||
// parallelCapable class loaders do NOT wait for parallel loads to complete
|
||||
// wait for parallel class loading and/or to check for circularity error for Xcomp when loading.
|
||||
static bool needs_load_placeholder(Handle class_loader) {
|
||||
return class_loader.is_null() || !is_parallelCapable(class_loader);
|
||||
}
|
||||
|
||||
// For bootstrap and non-parallelCapable class loaders, check and wait for
|
||||
// another thread to complete loading this class.
|
||||
InstanceKlass* SystemDictionary::handle_parallel_loading(JavaThread* current,
|
||||
Symbol* name,
|
||||
ClassLoaderData* loader_data,
|
||||
Handle lockObject,
|
||||
bool* throw_circularity_error) {
|
||||
// Check for other threads loading this class either to throw CCE or wait in the case of the boot loader.
|
||||
static InstanceKlass* handle_parallel_loading(JavaThread* current,
|
||||
Symbol* name,
|
||||
ClassLoaderData* loader_data,
|
||||
bool must_wait_for_class_loading,
|
||||
bool* throw_circularity_error) {
|
||||
PlaceholderEntry* oldprobe = PlaceholderTable::get_entry(name, loader_data);
|
||||
if (oldprobe != nullptr) {
|
||||
// -Xcomp calls load_signature_classes which might result in loading
|
||||
@ -577,32 +530,15 @@ InstanceKlass* SystemDictionary::handle_parallel_loading(JavaThread* current,
|
||||
log_circularity_error(name, oldprobe);
|
||||
*throw_circularity_error = true;
|
||||
return nullptr;
|
||||
} else {
|
||||
} else if (must_wait_for_class_loading) {
|
||||
// Wait until the first thread has finished loading this class. Also wait until all the
|
||||
// threads trying to load its superclass have removed their placeholders.
|
||||
while (oldprobe != nullptr &&
|
||||
(oldprobe->instance_load_in_progress() || oldprobe->super_load_in_progress())) {
|
||||
|
||||
// We only get here if the application has released the
|
||||
// classloader lock when another thread was in the middle of loading a
|
||||
// superclass/superinterface for this class, and now
|
||||
// this thread is also trying to load this class.
|
||||
// To minimize surprises, the first thread that started to
|
||||
// load a class should be the one to complete the loading
|
||||
// with the classfile it initially expected.
|
||||
// This logic has the current thread wait once it has done
|
||||
// all the superclass/superinterface loading it can, until
|
||||
// the original thread completes the class loading or fails
|
||||
// If it completes we will use the resulting InstanceKlass
|
||||
// which we will find below in the systemDictionary.
|
||||
|
||||
if (lockObject.is_null()) {
|
||||
SystemDictionary_lock->wait();
|
||||
} else if (EnableWaitForParallelLoad) {
|
||||
double_lock_wait(current, lockObject);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
// LOAD_INSTANCE placeholders are used to implement parallel capable class loading
|
||||
// for the bootclass loader.
|
||||
SystemDictionary_lock->wait();
|
||||
|
||||
// Check if classloading completed while we were waiting
|
||||
InstanceKlass* check = loader_data->dictionary()->find_class(current, name);
|
||||
@ -709,7 +645,6 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
||||
bool load_placeholder_added = false;
|
||||
|
||||
// Add placeholder entry to record loading instance class
|
||||
// Four cases:
|
||||
// case 1. Bootstrap classloader
|
||||
// This classloader supports parallelism at the classloader level
|
||||
// but only allows a single thread to load a class/classloader pair.
|
||||
@ -718,20 +653,16 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
||||
// These class loaders lock a per-class object lock when ClassLoader.loadClass()
|
||||
// is called. A LOAD_INSTANCE placeholder isn't used for mutual exclusion.
|
||||
// case 3. traditional classloaders that rely on the classloader object lock
|
||||
// There should be no need for need for LOAD_INSTANCE, except:
|
||||
// case 4. traditional class loaders that break the classloader object lock
|
||||
// as a legacy deadlock workaround. Detection of this case requires that
|
||||
// this check is done while holding the classloader object lock,
|
||||
// and that lock is still held when calling classloader's loadClass.
|
||||
// For these classloaders, we ensure that the first requestor
|
||||
// completes the load and other requestors wait for completion.
|
||||
// There should be no need for need for LOAD_INSTANCE for mutual exclusion,
|
||||
// except the LOAD_INSTANCE placeholder is used to detect CCE for -Xcomp.
|
||||
// TODO: should also be used to detect CCE for parallel capable class loaders but it's not.
|
||||
{
|
||||
MutexLocker mu(THREAD, SystemDictionary_lock);
|
||||
if (needs_load_placeholder(class_loader)) {
|
||||
loaded_class = handle_parallel_loading(THREAD,
|
||||
name,
|
||||
loader_data,
|
||||
lockObject,
|
||||
class_loader.is_null(),
|
||||
&throw_circularity_error);
|
||||
}
|
||||
|
||||
@ -742,8 +673,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
||||
if (check != nullptr) {
|
||||
loaded_class = check;
|
||||
} else if (needs_load_placeholder(class_loader)) {
|
||||
// Add the LOAD_INSTANCE token. Threads will wait on loading to complete for this thread,
|
||||
// and check for ClassCircularityError with -Xcomp.
|
||||
// Add the LOAD_INSTANCE token. Threads will wait on loading to complete for this thread.
|
||||
PlaceholderEntry* newprobe = PlaceholderTable::find_and_add(name, loader_data,
|
||||
PlaceholderTable::LOAD_INSTANCE,
|
||||
nullptr,
|
||||
|
||||
@ -302,12 +302,6 @@ private:
|
||||
static Klass* resolve_array_class_or_null(Symbol* class_name,
|
||||
Handle class_loader,
|
||||
Handle protection_domain, TRAPS);
|
||||
static InstanceKlass* handle_parallel_loading(JavaThread* current,
|
||||
Symbol* name,
|
||||
ClassLoaderData* loader_data,
|
||||
Handle lockObject,
|
||||
bool* throw_circularity_error);
|
||||
|
||||
static void define_instance_class(InstanceKlass* k, Handle class_loader, TRAPS);
|
||||
static InstanceKlass* find_or_define_helper(Symbol* class_name,
|
||||
Handle class_loader,
|
||||
|
||||
@ -542,7 +542,6 @@ static SpecialFlag const special_jvm_flags[] = {
|
||||
{ "DynamicDumpSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() },
|
||||
{ "RequireSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() },
|
||||
{ "UseSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() },
|
||||
{ "EnableWaitForParallelLoad", JDK_Version::jdk(20), JDK_Version::jdk(21), JDK_Version::jdk(22) },
|
||||
|
||||
// --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in:
|
||||
{ "DefaultMaxRAMFraction", JDK_Version::jdk(8), JDK_Version::undefined(), JDK_Version::undefined() },
|
||||
@ -551,6 +550,7 @@ static SpecialFlag const special_jvm_flags[] = {
|
||||
|
||||
// -------------- Obsolete Flags - sorted by expired_in --------------
|
||||
|
||||
{ "EnableWaitForParallelLoad", JDK_Version::jdk(20), JDK_Version::jdk(21), JDK_Version::jdk(22) },
|
||||
{ "G1ConcRefinementGreenZone", JDK_Version::undefined(), JDK_Version::jdk(20), JDK_Version::undefined() },
|
||||
{ "G1ConcRefinementYellowZone", JDK_Version::undefined(), JDK_Version::jdk(20), JDK_Version::undefined() },
|
||||
{ "G1ConcRefinementRedZone", JDK_Version::undefined(), JDK_Version::jdk(20), JDK_Version::undefined() },
|
||||
|
||||
@ -688,10 +688,6 @@ const int ObjectAlignmentInBytes = 8;
|
||||
"Allow parallel defineClass requests for class loaders " \
|
||||
"registering as parallel capable") \
|
||||
\
|
||||
product(bool, EnableWaitForParallelLoad, false, \
|
||||
"(Deprecated) Enable legacy parallel classloading logic for " \
|
||||
"class loaders not registered as parallel capable") \
|
||||
\
|
||||
product_pd(bool, DontYieldALot, \
|
||||
"Throw away obvious excess yield calls") \
|
||||
\
|
||||
|
||||
@ -1340,12 +1340,7 @@ void ObjectMonitor::ExitEpilog(JavaThread* current, ObjectWaiter* Wakee) {
|
||||
OM_PERFDATA_OP(Parks, inc());
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Class Loader deadlock handling.
|
||||
//
|
||||
// complete_exit exits a lock returning recursion count
|
||||
// complete_exit/reenter operate as a wait without waiting
|
||||
// complete_exit requires an inflated monitor
|
||||
// The _owner field is not always the Thread addr even with an
|
||||
// inflated monitor, e.g. the monitor can be inflated by a non-owning
|
||||
@ -1370,20 +1365,6 @@ intx ObjectMonitor::complete_exit(JavaThread* current) {
|
||||
return save;
|
||||
}
|
||||
|
||||
// reenter() enters a lock and sets recursion count
|
||||
// complete_exit/reenter operate as a wait without waiting
|
||||
bool ObjectMonitor::reenter(intx recursions, JavaThread* current) {
|
||||
|
||||
guarantee(owner_raw() != current, "reenter already owner");
|
||||
if (!enter(current)) {
|
||||
return false;
|
||||
}
|
||||
// Entered the monitor.
|
||||
guarantee(_recursions == 0, "reenter recursion");
|
||||
_recursions = recursions;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Checks that the current THREAD owns this monitor and causes an
|
||||
// immediate return if it doesn't. We don't use the CHECK macro
|
||||
// because we want the IMSE to be the only exception that is thrown
|
||||
|
||||
@ -321,7 +321,6 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
|
||||
|
||||
// Use the following at your own risk
|
||||
intx complete_exit(JavaThread* current);
|
||||
bool reenter(intx recursions, JavaThread* current);
|
||||
|
||||
private:
|
||||
void AddWaiter(ObjectWaiter* waiter);
|
||||
|
||||
@ -575,42 +575,6 @@ void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current)
|
||||
monitor->exit(current);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Class Loader support to workaround deadlocks on the class loader lock objects
|
||||
// Also used by GC
|
||||
// complete_exit()/reenter() are used to wait on a nested lock
|
||||
// i.e. to give up an outer lock completely and then re-enter
|
||||
// Used when holding nested locks - lock acquisition order: lock1 then lock2
|
||||
// 1) complete_exit lock1 - saving recursion count
|
||||
// 2) wait on lock2
|
||||
// 3) when notified on lock2, unlock lock2
|
||||
// 4) reenter lock1 with original recursion count
|
||||
// 5) lock lock2
|
||||
// NOTE: must use heavy weight monitor to handle complete_exit/reenter()
|
||||
intx ObjectSynchronizer::complete_exit(Handle obj, JavaThread* current) {
|
||||
// The ObjectMonitor* can't be async deflated until ownership is
|
||||
// dropped inside exit() and the ObjectMonitor* must be !is_busy().
|
||||
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_vm_internal);
|
||||
intx recur_count = monitor->complete_exit(current);
|
||||
current->dec_held_monitor_count(recur_count + 1);
|
||||
return recur_count;
|
||||
}
|
||||
|
||||
// NOTE: must use heavy weight monitor to handle complete_exit/reenter()
|
||||
void ObjectSynchronizer::reenter(Handle obj, intx recursions, JavaThread* current) {
|
||||
// An async deflation can race after the inflate() call and before
|
||||
// reenter() -> enter() can make the ObjectMonitor busy. reenter() ->
|
||||
// enter() returns false if we have lost the race to async deflation
|
||||
// and we simply try again.
|
||||
while (true) {
|
||||
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_vm_internal);
|
||||
if (monitor->reenter(recursions, current)) {
|
||||
current->inc_held_monitor_count(recursions + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// JNI locks on java objects
|
||||
// NOTE: must use heavy weight monitor to handle jni monitor enter
|
||||
|
||||
@ -154,12 +154,6 @@ class ObjectSynchronizer : AllStatic {
|
||||
static bool quick_notify(oopDesc* obj, JavaThread* current, bool All);
|
||||
static bool quick_enter(oop obj, JavaThread* current, BasicLock* Lock);
|
||||
|
||||
// used by classloading to free classloader object lock,
|
||||
// wait on an internal lock, and reclaim original lock
|
||||
// with original recursion count
|
||||
static intx complete_exit(Handle obj, JavaThread* current);
|
||||
static void reenter (Handle obj, intx recursions, JavaThread* current);
|
||||
|
||||
// Inflate light weight monitor to heavy weight monitor
|
||||
static ObjectMonitor* inflate(Thread* current, oop obj, const InflateCause cause);
|
||||
// This version is only for internal use
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
* @compile -XDignore.symbol.file AsmClasses.java
|
||||
* @compile test-classes/ClassInLoader.java test-classes/A.java test-classes/B.java ../share/ThreadPrint.java
|
||||
* @run main/othervm ParallelSuperTest
|
||||
* @run main/othervm -XX:+EnableWaitForParallelLoad ParallelSuperTest -parallel
|
||||
* @run main/othervm ParallelSuperTest -parallel -parallelCapable
|
||||
*/
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user