mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
8288064: Class initialization locking
Reviewed-by: rehn, vlivanov
This commit is contained in:
parent
3d12c0225b
commit
cf4a4966a8
14 changed files with 140 additions and 172 deletions
|
@ -495,6 +495,10 @@ Array<int>* InstanceKlass::create_new_default_vtable_indices(int len, TRAPS) {
|
|||
return vtable_indices;
|
||||
}
|
||||
|
||||
static Monitor* create_init_monitor(const char* name) {
|
||||
return new Monitor(Mutex::safepoint, name);
|
||||
}
|
||||
|
||||
InstanceKlass::InstanceKlass(const ClassFileParser& parser, KlassKind kind) :
|
||||
Klass(kind),
|
||||
_nest_members(NULL),
|
||||
|
@ -507,6 +511,7 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, KlassKind kind) :
|
|||
_nest_host_index(0),
|
||||
_init_state(allocated),
|
||||
_reference_type(parser.reference_type()),
|
||||
_init_monitor(create_init_monitor("InstanceKlassInitMonitor_lock")),
|
||||
_init_thread(NULL)
|
||||
{
|
||||
set_vtable_length(parser.vtable_size());
|
||||
|
@ -724,28 +729,6 @@ objArrayOop InstanceKlass::signers() const {
|
|||
return java_lang_Class::signers(java_mirror());
|
||||
}
|
||||
|
||||
oop InstanceKlass::init_lock() const {
|
||||
// return the init lock from the mirror
|
||||
oop lock = java_lang_Class::init_lock(java_mirror());
|
||||
// Prevent reordering with any access of initialization state
|
||||
OrderAccess::loadload();
|
||||
assert(lock != NULL || !is_not_initialized(), // initialized or in_error state
|
||||
"only fully initialized state can have a null lock");
|
||||
return lock;
|
||||
}
|
||||
|
||||
// Set the initialization lock to null so the object can be GC'ed. Any racing
|
||||
// threads to get this lock will see a null lock and will not lock.
|
||||
// That's okay because they all check for initialized state after getting
|
||||
// the lock and return.
|
||||
void InstanceKlass::fence_and_clear_init_lock() {
|
||||
// make sure previous stores are all done, notably the init_state.
|
||||
OrderAccess::storestore();
|
||||
java_lang_Class::clear_init_lock(java_mirror());
|
||||
assert(!is_not_initialized(), "class must be initialized now");
|
||||
}
|
||||
|
||||
|
||||
// See "The Virtual Machine Specification" section 2.16.5 for a detailed explanation of the class initialization
|
||||
// process. The step comments refers to the procedure described in that section.
|
||||
// Note: implementation moved to static method to expose the this pointer.
|
||||
|
@ -773,6 +756,26 @@ void InstanceKlass::link_class(TRAPS) {
|
|||
}
|
||||
}
|
||||
|
||||
void InstanceKlass::check_link_state_and_wait(JavaThread* current) {
|
||||
MonitorLocker ml(current, _init_monitor);
|
||||
|
||||
// Another thread is linking this class, wait.
|
||||
while (is_being_linked() && !is_init_thread(current)) {
|
||||
ml.wait();
|
||||
}
|
||||
|
||||
// This thread is recursively linking this class, continue
|
||||
if (is_being_linked() && is_init_thread(current)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this class wasn't linked already, set state to being_linked
|
||||
if (!is_linked()) {
|
||||
set_init_state(being_linked);
|
||||
set_init_thread(current);
|
||||
}
|
||||
}
|
||||
|
||||
// Called to verify that a class can link during initialization, without
|
||||
// throwing a VerifyError.
|
||||
bool InstanceKlass::link_class_or_fail(TRAPS) {
|
||||
|
@ -851,9 +854,8 @@ bool InstanceKlass::link_class_impl(TRAPS) {
|
|||
|
||||
// verification & rewriting
|
||||
{
|
||||
HandleMark hm(THREAD);
|
||||
Handle h_init_lock(THREAD, init_lock());
|
||||
ObjectLocker ol(h_init_lock, jt);
|
||||
LockLinkState init_lock(this, jt);
|
||||
|
||||
// rewritten will have been set if loader constraint error found
|
||||
// on an earlier link attempt
|
||||
// don't verify or rewrite if already rewritten
|
||||
|
@ -911,17 +913,7 @@ bool InstanceKlass::link_class_impl(TRAPS) {
|
|||
// In case itable verification is ever added.
|
||||
// itable().verify(tty, true);
|
||||
#endif
|
||||
if (UseVtableBasedCHA) {
|
||||
MutexLocker ml(THREAD, Compile_lock);
|
||||
set_init_state(linked);
|
||||
|
||||
// Now flush all code that assume the class is not linked.
|
||||
if (Universe::is_fully_initialized()) {
|
||||
CodeCache::flush_dependents_on(this);
|
||||
}
|
||||
} else {
|
||||
set_init_state(linked);
|
||||
}
|
||||
set_initialization_state_and_notify(linked, THREAD);
|
||||
if (JvmtiExport::should_post_class_prepare()) {
|
||||
JvmtiExport::post_class_prepare(THREAD, this);
|
||||
}
|
||||
|
@ -1034,28 +1026,25 @@ void InstanceKlass::initialize_impl(TRAPS) {
|
|||
DTRACE_CLASSINIT_PROBE(required, -1);
|
||||
|
||||
bool wait = false;
|
||||
bool throw_error = false;
|
||||
|
||||
JavaThread* jt = THREAD;
|
||||
|
||||
// refer to the JVM book page 47 for description of steps
|
||||
// Step 1
|
||||
{
|
||||
Handle h_init_lock(THREAD, init_lock());
|
||||
ObjectLocker ol(h_init_lock, jt);
|
||||
MonitorLocker ml(THREAD, _init_monitor);
|
||||
|
||||
// Step 2
|
||||
// If we were to use wait() instead of waitInterruptibly() then
|
||||
// we might end up throwing IE from link/symbol resolution sites
|
||||
// that aren't expected to throw. This would wreak havoc. See 6320309.
|
||||
while (is_being_initialized() && !is_reentrant_initialization(jt)) {
|
||||
while (is_being_initialized() && !is_init_thread(jt)) {
|
||||
wait = true;
|
||||
jt->set_class_to_be_initialized(this);
|
||||
ol.wait_uninterruptibly(jt);
|
||||
ml.wait();
|
||||
jt->set_class_to_be_initialized(NULL);
|
||||
}
|
||||
|
||||
// Step 3
|
||||
if (is_being_initialized() && is_reentrant_initialization(jt)) {
|
||||
if (is_being_initialized() && is_init_thread(jt)) {
|
||||
DTRACE_CLASSINIT_PROBE_WAIT(recursive, -1, wait);
|
||||
return;
|
||||
}
|
||||
|
@ -1068,23 +1057,29 @@ void InstanceKlass::initialize_impl(TRAPS) {
|
|||
|
||||
// Step 5
|
||||
if (is_in_error_state()) {
|
||||
DTRACE_CLASSINIT_PROBE_WAIT(erroneous, -1, wait);
|
||||
ResourceMark rm(THREAD);
|
||||
Handle cause(THREAD, get_initialization_error(THREAD));
|
||||
throw_error = true;
|
||||
} else {
|
||||
|
||||
stringStream ss;
|
||||
ss.print("Could not initialize class %s", external_name());
|
||||
if (cause.is_null()) {
|
||||
THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), ss.as_string());
|
||||
} else {
|
||||
THROW_MSG_CAUSE(vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
ss.as_string(), cause);
|
||||
}
|
||||
// Step 6
|
||||
set_init_state(being_initialized);
|
||||
set_init_thread(jt);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6
|
||||
set_init_state(being_initialized);
|
||||
set_init_thread(jt);
|
||||
// Throw error outside lock
|
||||
if (throw_error) {
|
||||
DTRACE_CLASSINIT_PROBE_WAIT(erroneous, -1, wait);
|
||||
ResourceMark rm(THREAD);
|
||||
Handle cause(THREAD, get_initialization_error(THREAD));
|
||||
|
||||
stringStream ss;
|
||||
ss.print("Could not initialize class %s", external_name());
|
||||
if (cause.is_null()) {
|
||||
THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), ss.as_string());
|
||||
} else {
|
||||
THROW_MSG_CAUSE(vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
ss.as_string(), cause);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 7
|
||||
|
@ -1144,7 +1139,7 @@ void InstanceKlass::initialize_impl(TRAPS) {
|
|||
|
||||
// Step 9
|
||||
if (!HAS_PENDING_EXCEPTION) {
|
||||
set_initialization_state_and_notify(fully_initialized, CHECK);
|
||||
set_initialization_state_and_notify(fully_initialized, THREAD);
|
||||
debug_only(vtable().verify(tty, true);)
|
||||
}
|
||||
else {
|
||||
|
@ -1177,19 +1172,23 @@ void InstanceKlass::initialize_impl(TRAPS) {
|
|||
}
|
||||
|
||||
|
||||
void InstanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS) {
|
||||
Handle h_init_lock(THREAD, init_lock());
|
||||
if (h_init_lock() != NULL) {
|
||||
ObjectLocker ol(h_init_lock, THREAD);
|
||||
void InstanceKlass::set_initialization_state_and_notify(ClassState state, JavaThread* current) {
|
||||
MonitorLocker ml(current, _init_monitor);
|
||||
|
||||
// Now flush all code that assume the class is not linked.
|
||||
// Set state under the Compile_lock also.
|
||||
if (state == linked && UseVtableBasedCHA && Universe::is_fully_initialized()) {
|
||||
MutexLocker ml(current, Compile_lock);
|
||||
|
||||
set_init_thread(NULL); // reset _init_thread before changing _init_state
|
||||
set_init_state(state);
|
||||
fence_and_clear_init_lock();
|
||||
ol.notify_all(CHECK);
|
||||
|
||||
CodeCache::flush_dependents_on(this);
|
||||
} else {
|
||||
assert(h_init_lock() != NULL, "The initialization state should never be set twice");
|
||||
set_init_thread(NULL); // reset _init_thread before changing _init_state
|
||||
set_init_state(state);
|
||||
}
|
||||
ml.notify_all();
|
||||
}
|
||||
|
||||
InstanceKlass* InstanceKlass::implementor() const {
|
||||
|
@ -2518,6 +2517,7 @@ void InstanceKlass::remove_unshareable_info() {
|
|||
_nest_host = NULL;
|
||||
init_shared_package_entry();
|
||||
_dep_context_last_cleaned = 0;
|
||||
_init_monitor = NULL;
|
||||
}
|
||||
|
||||
void InstanceKlass::remove_java_mirror() {
|
||||
|
@ -2597,6 +2597,9 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
|
|||
if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation()) {
|
||||
set_is_value_based();
|
||||
}
|
||||
|
||||
// restore the monitor
|
||||
_init_monitor = create_init_monitor("InstanceKlassInitMonitorRestored_lock");
|
||||
}
|
||||
|
||||
// Check if a class or any of its supertypes has a version older than 50.
|
||||
|
@ -2704,6 +2707,9 @@ void InstanceKlass::release_C_heap_structures(bool release_constant_pool) {
|
|||
// Deallocate and call destructors for MDO mutexes
|
||||
methods_do(method_release_C_heap_structures);
|
||||
|
||||
// Destroy the init_monitor
|
||||
delete _init_monitor;
|
||||
|
||||
// Deallocate oop map cache
|
||||
if (_oop_map_cache != NULL) {
|
||||
delete _oop_map_cache;
|
||||
|
@ -3377,7 +3383,7 @@ nmethod* InstanceKlass::lookup_osr_nmethod(const Method* m, int bci, int comp_le
|
|||
#define BULLET " - "
|
||||
|
||||
static const char* state_names[] = {
|
||||
"allocated", "loaded", "linked", "being_initialized", "fully_initialized", "initialization_error"
|
||||
"allocated", "loaded", "being_linked", "linked", "being_initialized", "fully_initialized", "initialization_error"
|
||||
};
|
||||
|
||||
static void print_vtable(intptr_t* start, int len, outputStream* st) {
|
||||
|
@ -3931,13 +3937,17 @@ void JNIid::verify(Klass* holder) {
|
|||
}
|
||||
|
||||
void InstanceKlass::set_init_state(ClassState state) {
|
||||
if (state > loaded) {
|
||||
assert_lock_strong(_init_monitor);
|
||||
}
|
||||
#ifdef ASSERT
|
||||
bool good_state = is_shared() ? (_init_state <= state)
|
||||
: (_init_state < state);
|
||||
assert(good_state || state == allocated, "illegal state transition");
|
||||
bool link_failed = _init_state == being_linked && state == loaded;
|
||||
assert(good_state || state == allocated || link_failed, "illegal state transition");
|
||||
#endif
|
||||
assert(_init_thread == NULL, "should be cleared before state change");
|
||||
_init_state = (u1)state;
|
||||
_init_state = state;
|
||||
}
|
||||
|
||||
#if INCLUDE_JVMTI
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue