8288064: Class initialization locking

Reviewed-by: rehn, vlivanov
This commit is contained in:
Coleen Phillimore 2022-06-16 12:38:06 +00:00
parent 3d12c0225b
commit cf4a4966a8
14 changed files with 140 additions and 172 deletions

View File

@ -792,7 +792,6 @@ int java_lang_Class::_class_loader_offset;
int java_lang_Class::_module_offset; int java_lang_Class::_module_offset;
int java_lang_Class::_protection_domain_offset; int java_lang_Class::_protection_domain_offset;
int java_lang_Class::_component_mirror_offset; int java_lang_Class::_component_mirror_offset;
int java_lang_Class::_init_lock_offset;
int java_lang_Class::_signers_offset; int java_lang_Class::_signers_offset;
int java_lang_Class::_name_offset; int java_lang_Class::_name_offset;
int java_lang_Class::_source_file_offset; int java_lang_Class::_source_file_offset;
@ -926,12 +925,6 @@ void java_lang_Class::initialize_mirror_fields(Klass* k,
Handle protection_domain, Handle protection_domain,
Handle classData, Handle classData,
TRAPS) { TRAPS) {
// Allocate a simple java object for a lock.
// This needs to be a java object because during class initialization
// it can be held across a java call.
typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK);
set_init_lock(mirror(), r);
// Set protection domain also // Set protection domain also
set_protection_domain(mirror(), protection_domain()); set_protection_domain(mirror(), protection_domain());
@ -1270,8 +1263,6 @@ oop java_lang_Class::process_archived_mirror(Klass* k, oop mirror,
// Reset local static fields in the mirror // Reset local static fields in the mirror
InstanceKlass::cast(k)->do_local_static_fields(&reset); InstanceKlass::cast(k)->do_local_static_fields(&reset);
set_init_lock(archived_mirror, NULL);
set_protection_domain(archived_mirror, NULL); set_protection_domain(archived_mirror, NULL);
set_signers(archived_mirror, NULL); set_signers(archived_mirror, NULL);
set_source_file(archived_mirror, NULL); set_source_file(archived_mirror, NULL);
@ -1353,10 +1344,6 @@ bool java_lang_Class::restore_archived_mirror(Klass *k,
if (!k->is_array_klass()) { if (!k->is_array_klass()) {
// - local static final fields with initial values were initialized at dump time // - local static final fields with initial values were initialized at dump time
// create the init_lock
typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK_(false));
set_init_lock(mirror(), r);
if (protection_domain.not_null()) { if (protection_domain.not_null()) {
set_protection_domain(mirror(), protection_domain()); set_protection_domain(mirror(), protection_domain());
} }
@ -1421,15 +1408,6 @@ oop java_lang_Class::component_mirror(oop java_class) {
return java_class->obj_field(_component_mirror_offset); return java_class->obj_field(_component_mirror_offset);
} }
oop java_lang_Class::init_lock(oop java_class) {
assert(_init_lock_offset != 0, "must be set");
return java_class->obj_field(_init_lock_offset);
}
void java_lang_Class::set_init_lock(oop java_class, oop init_lock) {
assert(_init_lock_offset != 0, "must be set");
java_class->obj_field_put(_init_lock_offset, init_lock);
}
objArrayOop java_lang_Class::signers(oop java_class) { objArrayOop java_lang_Class::signers(oop java_class) {
assert(_signers_offset != 0, "must be set"); assert(_signers_offset != 0, "must be set");
return (objArrayOop)java_class->obj_field(_signers_offset); return (objArrayOop)java_class->obj_field(_signers_offset);
@ -1641,18 +1619,12 @@ void java_lang_Class::compute_offsets() {
InstanceKlass* k = vmClasses::Class_klass(); InstanceKlass* k = vmClasses::Class_klass();
CLASS_FIELDS_DO(FIELD_COMPUTE_OFFSET); CLASS_FIELDS_DO(FIELD_COMPUTE_OFFSET);
// Init lock is a C union with component_mirror. Only instanceKlass mirrors have
// init_lock and only ArrayKlass mirrors have component_mirror. Since both are oops
// GC treats them the same.
_init_lock_offset = _component_mirror_offset;
CLASS_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET); CLASS_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
} }
#if INCLUDE_CDS #if INCLUDE_CDS
void java_lang_Class::serialize_offsets(SerializeClosure* f) { void java_lang_Class::serialize_offsets(SerializeClosure* f) {
f->do_bool(&_offsets_computed); f->do_bool(&_offsets_computed);
f->do_u4((u4*)&_init_lock_offset);
CLASS_FIELDS_DO(FIELD_SERIALIZE_OFFSET); CLASS_FIELDS_DO(FIELD_SERIALIZE_OFFSET);

View File

@ -285,7 +285,6 @@ class java_lang_Class : AllStatic {
static int _static_oop_field_count_offset; static int _static_oop_field_count_offset;
static int _protection_domain_offset; static int _protection_domain_offset;
static int _init_lock_offset;
static int _signers_offset; static int _signers_offset;
static int _class_loader_offset; static int _class_loader_offset;
static int _module_offset; static int _module_offset;
@ -300,7 +299,6 @@ class java_lang_Class : AllStatic {
static GrowableArray<Klass*>* _fixup_mirror_list; static GrowableArray<Klass*>* _fixup_mirror_list;
static GrowableArray<Klass*>* _fixup_module_field_list; static GrowableArray<Klass*>* _fixup_module_field_list;
static void set_init_lock(oop java_class, oop init_lock);
static void set_protection_domain(oop java_class, oop protection_domain); static void set_protection_domain(oop java_class, oop protection_domain);
static void set_class_loader(oop java_class, oop class_loader); static void set_class_loader(oop java_class, oop class_loader);
static void set_component_mirror(oop java_class, oop comp_mirror); static void set_component_mirror(oop java_class, oop comp_mirror);
@ -356,10 +354,6 @@ class java_lang_Class : AllStatic {
// Support for embedded per-class oops // Support for embedded per-class oops
static oop protection_domain(oop java_class); static oop protection_domain(oop java_class);
static oop init_lock(oop java_class);
static void clear_init_lock(oop java_class) {
set_init_lock(java_class, NULL);
}
static oop component_mirror(oop java_class); static oop component_mirror(oop java_class);
static objArrayOop signers(oop java_class); static objArrayOop signers(oop java_class);
static void set_signers(oop java_class, objArrayOop signers); static void set_signers(oop java_class, objArrayOop signers);

View File

@ -1774,7 +1774,7 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
// the interpreter or runtime performs a serialized check of // the interpreter or runtime performs a serialized check of
// the relevant CPCE::f1 field. This is done by the caller // the relevant CPCE::f1 field. This is done by the caller
// of this method, via CPCE::set_dynamic_call, which uses // of this method, via CPCE::set_dynamic_call, which uses
// an ObjectLocker to do the final serialization of updates // a lock to do the final serialization of updates
// to CPCE state, including f1. // to CPCE state, including f1.
// Log dynamic info to CDS classlist. // Log dynamic info to CDS classlist.

View File

@ -156,7 +156,7 @@
\ \
nonstatic_field(InstanceKlass, _fields, Array<u2>*) \ nonstatic_field(InstanceKlass, _fields, Array<u2>*) \
nonstatic_field(InstanceKlass, _constants, ConstantPool*) \ nonstatic_field(InstanceKlass, _constants, ConstantPool*) \
nonstatic_field(InstanceKlass, _init_state, u1) \ nonstatic_field(InstanceKlass, _init_state, InstanceKlass::ClassState) \
nonstatic_field(InstanceKlass, _init_thread, Thread*) \ nonstatic_field(InstanceKlass, _init_thread, Thread*) \
nonstatic_field(InstanceKlass, _misc_flags, u2) \ nonstatic_field(InstanceKlass, _misc_flags, u2) \
nonstatic_field(InstanceKlass, _annotations, Annotations*) \ nonstatic_field(InstanceKlass, _annotations, Annotations*) \

View File

@ -266,7 +266,7 @@ void ConstantPoolCacheEntry::set_direct_or_vtable_call(Bytecodes::Code invoke_co
} }
if (invoke_code == Bytecodes::_invokestatic) { if (invoke_code == Bytecodes::_invokestatic) {
assert(method->method_holder()->is_initialized() || assert(method->method_holder()->is_initialized() ||
method->method_holder()->is_reentrant_initialization(Thread::current()), method->method_holder()->is_init_thread(Thread::current()),
"invalid class initialization state for invoke_static"); "invalid class initialization state for invoke_static");
if (!VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { if (!VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) {
@ -373,15 +373,9 @@ void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle&
// A losing writer waits on the lock until the winner writes f1 and leaves // A losing writer waits on the lock until the winner writes f1 and leaves
// the lock, so that when the losing writer returns, he can use the linked // the lock, so that when the losing writer returns, he can use the linked
// cache entry. // cache entry.
// Lock fields to write
MutexLocker ml(cpool->pool_holder()->init_monitor());
JavaThread* current = JavaThread::current();
objArrayHandle resolved_references(current, cpool->resolved_references());
// Use the resolved_references() lock for this cpCache entry.
// resolved_references are created for all classes with Invokedynamic, MethodHandle
// or MethodType constant pool cache entries.
assert(resolved_references() != NULL,
"a resolved_references array should have been created for this class");
ObjectLocker ol(resolved_references, current);
if (!is_f1_null()) { if (!is_f1_null()) {
return; return;
} }
@ -453,6 +447,7 @@ void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle&
// Store appendix, if any. // Store appendix, if any.
if (has_appendix) { if (has_appendix) {
const int appendix_index = f2_as_index(); const int appendix_index = f2_as_index();
objArrayOop resolved_references = cpool->resolved_references();
assert(appendix_index >= 0 && appendix_index < resolved_references->length(), "oob"); assert(appendix_index >= 0 && appendix_index < resolved_references->length(), "oob");
assert(resolved_references->obj_at(appendix_index) == NULL, "init just once"); assert(resolved_references->obj_at(appendix_index) == NULL, "init just once");
resolved_references->obj_at_put(appendix_index, appendix()); resolved_references->obj_at_put(appendix_index, appendix());
@ -480,14 +475,7 @@ bool ConstantPoolCacheEntry::save_and_throw_indy_exc(
assert(PENDING_EXCEPTION->is_a(vmClasses::LinkageError_klass()), assert(PENDING_EXCEPTION->is_a(vmClasses::LinkageError_klass()),
"No LinkageError exception"); "No LinkageError exception");
// Use the resolved_references() lock for this cpCache entry. MutexLocker ml(THREAD, cpool->pool_holder()->init_monitor());
// resolved_references are created for all classes with Invokedynamic, MethodHandle
// or MethodType constant pool cache entries.
JavaThread* current = THREAD;
objArrayHandle resolved_references(current, cpool->resolved_references());
assert(resolved_references() != NULL,
"a resolved_references array should have been created for this class");
ObjectLocker ol(resolved_references, current);
// if f1 is not null or the indy_resolution_failed flag is set then another // if f1 is not null or the indy_resolution_failed flag is set then another
// thread either succeeded in resolving the method or got a LinkageError // thread either succeeded in resolving the method or got a LinkageError

View File

@ -495,6 +495,10 @@ Array<int>* InstanceKlass::create_new_default_vtable_indices(int len, TRAPS) {
return vtable_indices; return vtable_indices;
} }
static Monitor* create_init_monitor(const char* name) {
return new Monitor(Mutex::safepoint, name);
}
InstanceKlass::InstanceKlass(const ClassFileParser& parser, KlassKind kind) : InstanceKlass::InstanceKlass(const ClassFileParser& parser, KlassKind kind) :
Klass(kind), Klass(kind),
_nest_members(NULL), _nest_members(NULL),
@ -507,6 +511,7 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, KlassKind kind) :
_nest_host_index(0), _nest_host_index(0),
_init_state(allocated), _init_state(allocated),
_reference_type(parser.reference_type()), _reference_type(parser.reference_type()),
_init_monitor(create_init_monitor("InstanceKlassInitMonitor_lock")),
_init_thread(NULL) _init_thread(NULL)
{ {
set_vtable_length(parser.vtable_size()); set_vtable_length(parser.vtable_size());
@ -724,28 +729,6 @@ objArrayOop InstanceKlass::signers() const {
return java_lang_Class::signers(java_mirror()); 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 // 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. // process. The step comments refers to the procedure described in that section.
// Note: implementation moved to static method to expose the this pointer. // 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 // Called to verify that a class can link during initialization, without
// throwing a VerifyError. // throwing a VerifyError.
bool InstanceKlass::link_class_or_fail(TRAPS) { bool InstanceKlass::link_class_or_fail(TRAPS) {
@ -851,9 +854,8 @@ bool InstanceKlass::link_class_impl(TRAPS) {
// verification & rewriting // verification & rewriting
{ {
HandleMark hm(THREAD); LockLinkState init_lock(this, jt);
Handle h_init_lock(THREAD, init_lock());
ObjectLocker ol(h_init_lock, jt);
// rewritten will have been set if loader constraint error found // rewritten will have been set if loader constraint error found
// on an earlier link attempt // on an earlier link attempt
// don't verify or rewrite if already rewritten // 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. // In case itable verification is ever added.
// itable().verify(tty, true); // itable().verify(tty, true);
#endif #endif
if (UseVtableBasedCHA) { set_initialization_state_and_notify(linked, THREAD);
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);
}
if (JvmtiExport::should_post_class_prepare()) { if (JvmtiExport::should_post_class_prepare()) {
JvmtiExport::post_class_prepare(THREAD, this); JvmtiExport::post_class_prepare(THREAD, this);
} }
@ -1034,28 +1026,25 @@ void InstanceKlass::initialize_impl(TRAPS) {
DTRACE_CLASSINIT_PROBE(required, -1); DTRACE_CLASSINIT_PROBE(required, -1);
bool wait = false; bool wait = false;
bool throw_error = false;
JavaThread* jt = THREAD; JavaThread* jt = THREAD;
// refer to the JVM book page 47 for description of steps // refer to the JVM book page 47 for description of steps
// Step 1 // Step 1
{ {
Handle h_init_lock(THREAD, init_lock()); MonitorLocker ml(THREAD, _init_monitor);
ObjectLocker ol(h_init_lock, jt);
// Step 2 // Step 2
// If we were to use wait() instead of waitInterruptibly() then while (is_being_initialized() && !is_init_thread(jt)) {
// 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)) {
wait = true; wait = true;
jt->set_class_to_be_initialized(this); jt->set_class_to_be_initialized(this);
ol.wait_uninterruptibly(jt); ml.wait();
jt->set_class_to_be_initialized(NULL); jt->set_class_to_be_initialized(NULL);
} }
// Step 3 // 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); DTRACE_CLASSINIT_PROBE_WAIT(recursive, -1, wait);
return; return;
} }
@ -1068,6 +1057,17 @@ void InstanceKlass::initialize_impl(TRAPS) {
// Step 5 // Step 5
if (is_in_error_state()) { if (is_in_error_state()) {
throw_error = true;
} else {
// 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); DTRACE_CLASSINIT_PROBE_WAIT(erroneous, -1, wait);
ResourceMark rm(THREAD); ResourceMark rm(THREAD);
Handle cause(THREAD, get_initialization_error(THREAD)); Handle cause(THREAD, get_initialization_error(THREAD));
@ -1082,11 +1082,6 @@ void InstanceKlass::initialize_impl(TRAPS) {
} }
} }
// Step 6
set_init_state(being_initialized);
set_init_thread(jt);
}
// Step 7 // Step 7
// Next, if C is a class rather than an interface, initialize it's super class and super // Next, if C is a class rather than an interface, initialize it's super class and super
// interfaces. // interfaces.
@ -1144,7 +1139,7 @@ void InstanceKlass::initialize_impl(TRAPS) {
// Step 9 // Step 9
if (!HAS_PENDING_EXCEPTION) { 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);) debug_only(vtable().verify(tty, true);)
} }
else { else {
@ -1177,19 +1172,23 @@ void InstanceKlass::initialize_impl(TRAPS) {
} }
void InstanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS) { void InstanceKlass::set_initialization_state_and_notify(ClassState state, JavaThread* current) {
Handle h_init_lock(THREAD, init_lock()); MonitorLocker ml(current, _init_monitor);
if (h_init_lock() != NULL) {
ObjectLocker ol(h_init_lock, THREAD); // 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_thread(NULL); // reset _init_thread before changing _init_state
set_init_state(state); set_init_state(state);
fence_and_clear_init_lock();
ol.notify_all(CHECK); CodeCache::flush_dependents_on(this);
} else { } 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_thread(NULL); // reset _init_thread before changing _init_state
set_init_state(state); set_init_state(state);
} }
ml.notify_all();
} }
InstanceKlass* InstanceKlass::implementor() const { InstanceKlass* InstanceKlass::implementor() const {
@ -2518,6 +2517,7 @@ void InstanceKlass::remove_unshareable_info() {
_nest_host = NULL; _nest_host = NULL;
init_shared_package_entry(); init_shared_package_entry();
_dep_context_last_cleaned = 0; _dep_context_last_cleaned = 0;
_init_monitor = NULL;
} }
void InstanceKlass::remove_java_mirror() { 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()) { if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation()) {
set_is_value_based(); 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. // 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 // Deallocate and call destructors for MDO mutexes
methods_do(method_release_C_heap_structures); methods_do(method_release_C_heap_structures);
// Destroy the init_monitor
delete _init_monitor;
// Deallocate oop map cache // Deallocate oop map cache
if (_oop_map_cache != NULL) { if (_oop_map_cache != NULL) {
delete _oop_map_cache; delete _oop_map_cache;
@ -3377,7 +3383,7 @@ nmethod* InstanceKlass::lookup_osr_nmethod(const Method* m, int bci, int comp_le
#define BULLET " - " #define BULLET " - "
static const char* state_names[] = { 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) { 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) { void InstanceKlass::set_init_state(ClassState state) {
if (state > loaded) {
assert_lock_strong(_init_monitor);
}
#ifdef ASSERT #ifdef ASSERT
bool good_state = is_shared() ? (_init_state <= state) bool good_state = is_shared() ? (_init_state <= state)
: (_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 #endif
assert(_init_thread == NULL, "should be cleared before state change"); assert(_init_thread == NULL, "should be cleared before state change");
_init_state = (u1)state; _init_state = state;
} }
#if INCLUDE_JVMTI #if INCLUDE_JVMTI

View File

@ -143,9 +143,10 @@ class InstanceKlass: public Klass {
// See "The Java Virtual Machine Specification" section 2.16.2-5 for a detailed description // See "The Java Virtual Machine Specification" section 2.16.2-5 for a detailed description
// of the class loading & initialization procedure, and the use of the states. // of the class loading & initialization procedure, and the use of the states.
enum ClassState { enum ClassState : u1 {
allocated, // allocated (but not yet linked) allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet) loaded, // loaded and inserted in class hierarchy (but not linked yet)
being_linked, // currently running verifier and rewriter
linked, // successfully linked/verified (but not initialized yet) linked, // successfully linked/verified (but not initialized yet)
being_initialized, // currently running class initializer being_initialized, // currently running class initializer
fully_initialized, // initialized (successful final state) fully_initialized, // initialized (successful final state)
@ -224,10 +225,7 @@ class InstanceKlass: public Klass {
// _misc_flags. // _misc_flags.
bool _is_marked_dependent; // used for marking during flushing and deoptimization bool _is_marked_dependent; // used for marking during flushing and deoptimization
// Class states are defined as ClassState (see above). ClassState _init_state; // state of class
// Place the _init_state here to utilize the unused 2-byte after
// _idnum_allocated_count.
u1 _init_state; // state of class
u1 _reference_type; // reference type u1 _reference_type; // reference type
@ -252,7 +250,9 @@ class InstanceKlass: public Klass {
} }
u2 _misc_flags; // There is more space in access_flags for more flags. u2 _misc_flags; // There is more space in access_flags for more flags.
Monitor* _init_monitor; // mutual exclusion to _init_state and _init_thread.
Thread* _init_thread; // Pointer to current thread doing initialization (to handle recursive initialization) Thread* _init_thread; // Pointer to current thread doing initialization (to handle recursive initialization)
OopMapCache* volatile _oop_map_cache; // OopMapCache for all methods in the klass (allocated lazily) OopMapCache* volatile _oop_map_cache; // OopMapCache for all methods in the klass (allocated lazily)
JNIid* _jni_ids; // First JNI identifier for static fields in this class JNIid* _jni_ids; // First JNI identifier for static fields in this class
jmethodID* volatile _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum, or NULL if none jmethodID* volatile _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum, or NULL if none
@ -536,17 +536,33 @@ public:
TRAPS); TRAPS);
public: public:
// initialization state // initialization state
bool is_loaded() const { return _init_state >= loaded; } bool is_loaded() const { return init_state() >= loaded; }
bool is_linked() const { return _init_state >= linked; } bool is_linked() const { return init_state() >= linked; }
bool is_initialized() const { return _init_state == fully_initialized; } bool is_being_linked() const { return init_state() == being_linked; }
bool is_not_initialized() const { return _init_state < being_initialized; } bool is_initialized() const { return init_state() == fully_initialized; }
bool is_being_initialized() const { return _init_state == being_initialized; } bool is_not_initialized() const { return init_state() < being_initialized; }
bool is_in_error_state() const { return _init_state == initialization_error; } bool is_being_initialized() const { return init_state() == being_initialized; }
bool is_reentrant_initialization(Thread *thread) { return thread == _init_thread; } bool is_in_error_state() const { return init_state() == initialization_error; }
ClassState init_state() { return (ClassState)_init_state; } bool is_init_thread(Thread *thread) { return thread == _init_thread; }
ClassState init_state() const { return Atomic::load(&_init_state); }
const char* init_state_name() const; const char* init_state_name() const;
bool is_rewritten() const { return (_misc_flags & _misc_rewritten) != 0; } bool is_rewritten() const { return (_misc_flags & _misc_rewritten) != 0; }
class LockLinkState : public StackObj {
InstanceKlass* _ik;
JavaThread* _current;
public:
LockLinkState(InstanceKlass* ik, JavaThread* current) : _ik(ik), _current(current) {
ik->check_link_state_and_wait(current);
}
~LockLinkState() {
if (!_ik->is_linked()) {
// Reset to loaded if linking failed.
_ik->set_initialization_state_and_notify(loaded, _current);
}
}
};
// is this a sealed class // is this a sealed class
bool is_sealed() const; bool is_sealed() const;
@ -911,7 +927,7 @@ public:
// initialization // initialization
void call_class_initializer(TRAPS); void call_class_initializer(TRAPS);
void set_initialization_state_and_notify(ClassState state, TRAPS); void set_initialization_state_and_notify(ClassState state, JavaThread* current);
// OopMapCache support // OopMapCache support
OopMapCache* oop_map_cache() { return _oop_map_cache; } OopMapCache* oop_map_cache() { return _oop_map_cache; }
@ -1142,7 +1158,10 @@ private:
// initialization state // initialization state
void set_init_state(ClassState state); void set_init_state(ClassState state);
void set_rewritten() { _misc_flags |= _misc_rewritten; } void set_rewritten() { _misc_flags |= _misc_rewritten; }
void set_init_thread(Thread *thread) { _init_thread = thread; } void set_init_thread(Thread *thread) {
assert(thread == nullptr || _init_thread == nullptr, "Only one thread is allowed to own initialization");
_init_thread = thread;
}
// The RedefineClasses() API can cause new method idnums to be needed // The RedefineClasses() API can cause new method idnums to be needed
// which will cause the caches to grow. Safety requires different // which will cause the caches to grow. Safety requires different
@ -1154,12 +1173,6 @@ private:
// Lock during initialization // Lock during initialization
public: public:
// Lock for (1) initialization; (2) access to the ConstantPool of this class.
// Must be one per class and it has to be a VM internal object so java code
// cannot lock it (like the mirror).
// It has to be an object not a Mutex because it's held through java calls.
oop init_lock() const;
// Returns the array class for the n'th dimension // Returns the array class for the n'th dimension
virtual Klass* array_klass(int n, TRAPS); virtual Klass* array_klass(int n, TRAPS);
virtual Klass* array_klass_or_null(int n); virtual Klass* array_klass_or_null(int n);
@ -1169,9 +1182,10 @@ public:
virtual Klass* array_klass_or_null(); virtual Klass* array_klass_or_null();
static void clean_initialization_error_table(); static void clean_initialization_error_table();
private:
void fence_and_clear_init_lock();
Monitor* init_monitor() const { return _init_monitor; }
private:
void check_link_state_and_wait(JavaThread* current);
bool link_class_impl (TRAPS); bool link_class_impl (TRAPS);
bool verify_code (TRAPS); bool verify_code (TRAPS);
void initialize_impl (TRAPS); void initialize_impl (TRAPS);

View File

@ -1405,7 +1405,7 @@ methodHandle SharedRuntime::resolve_sub_helper(bool is_virtual, bool is_optimize
if (invoke_code == Bytecodes::_invokestatic) { if (invoke_code == Bytecodes::_invokestatic) {
assert(callee_method->method_holder()->is_initialized() || assert(callee_method->method_holder()->is_initialized() ||
callee_method->method_holder()->is_reentrant_initialization(current), callee_method->method_holder()->is_init_thread(current),
"invalid class initialization state for invoke_static"); "invalid class initialization state for invoke_static");
if (!VM_Version::supports_fast_class_init_checks() && callee_method->needs_clinit_barrier()) { if (!VM_Version::supports_fast_class_init_checks() && callee_method->needs_clinit_barrier()) {
// In order to keep class initialization check, do not patch call // In order to keep class initialization check, do not patch call

View File

@ -226,9 +226,8 @@ void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) {
Klass* k = obj->klass(); Klass* k = obj->klass();
st->print_cr("\t- %s <" INTPTR_FORMAT "> (a %s)", "parking to wait for ", p2i(obj), k->external_name()); st->print_cr("\t- %s <" INTPTR_FORMAT "> (a %s)", "parking to wait for ", p2i(obj), k->external_name());
} }
else if (thread()->osthread()->get_state() == OBJECT_WAIT) { else if (thread()->osthread()->get_state() == CONDVAR_WAIT) {
// We are waiting on an Object monitor but Object.wait() isn't the // We are waiting on the native class initialization monitor.
// top-frame, so we should be waiting on a Class initialization monitor.
InstanceKlass* k = thread()->class_to_be_initialized(); InstanceKlass* k = thread()->class_to_be_initialized();
if (k != NULL) { if (k != NULL) {
st->print_cr("\t- waiting on the Class initialization monitor for %s", k->external_name()); st->print_cr("\t- waiting on the Class initialization monitor for %s", k->external_name());

View File

@ -238,7 +238,7 @@
nonstatic_field(InstanceKlass, _nonstatic_oop_map_size, int) \ nonstatic_field(InstanceKlass, _nonstatic_oop_map_size, int) \
nonstatic_field(InstanceKlass, _is_marked_dependent, bool) \ nonstatic_field(InstanceKlass, _is_marked_dependent, bool) \
nonstatic_field(InstanceKlass, _misc_flags, u2) \ nonstatic_field(InstanceKlass, _misc_flags, u2) \
nonstatic_field(InstanceKlass, _init_state, u1) \ nonstatic_field(InstanceKlass, _init_state, InstanceKlass::ClassState) \
nonstatic_field(InstanceKlass, _init_thread, Thread*) \ nonstatic_field(InstanceKlass, _init_thread, Thread*) \
nonstatic_field(InstanceKlass, _itable_len, int) \ nonstatic_field(InstanceKlass, _itable_len, int) \
nonstatic_field(InstanceKlass, _reference_type, u1) \ nonstatic_field(InstanceKlass, _reference_type, u1) \
@ -2280,6 +2280,7 @@
\ \
declare_constant(InstanceKlass::allocated) \ declare_constant(InstanceKlass::allocated) \
declare_constant(InstanceKlass::loaded) \ declare_constant(InstanceKlass::loaded) \
declare_constant(InstanceKlass::being_linked) \
declare_constant(InstanceKlass::linked) \ declare_constant(InstanceKlass::linked) \
declare_constant(InstanceKlass::being_initialized) \ declare_constant(InstanceKlass::being_initialized) \
declare_constant(InstanceKlass::fully_initialized) \ declare_constant(InstanceKlass::fully_initialized) \

View File

@ -1126,14 +1126,6 @@ u4 DumperSupport::get_static_fields_size(InstanceKlass* ik, u2& field_count) {
} }
} }
// Also provide a pointer to the init_lock if present, so there aren't unreferenced int[0]
// arrays.
oop init_lock = ik->init_lock();
if (init_lock != NULL) {
field_count++;
size += sizeof(address);
}
// We write the value itself plus a name and a one byte type tag per field. // We write the value itself plus a name and a one byte type tag per field.
return size + field_count * (sizeof(address) + 1); return size + field_count * (sizeof(address) + 1);
} }
@ -1171,14 +1163,6 @@ void DumperSupport::dump_static_fields(AbstractDumpWriter* writer, Klass* k) {
prev = prev->previous_versions(); prev = prev->previous_versions();
} }
} }
// Add init lock to the end if the class is not yet initialized
oop init_lock = ik->init_lock();
if (init_lock != NULL) {
writer->write_symbolID(vmSymbols::init_lock_name()); // name
writer->write_u1(sig2tag(vmSymbols::int_array_signature())); // type
writer->write_objectID(init_lock);
}
} }
// dump the raw values of the instance fields of the given object // dump the raw values of the instance fields of the given object

View File

@ -60,6 +60,7 @@ public class InstanceKlass extends Klass {
// ClassState constants // ClassState constants
private static int CLASS_STATE_ALLOCATED; private static int CLASS_STATE_ALLOCATED;
private static int CLASS_STATE_LOADED; private static int CLASS_STATE_LOADED;
private static int CLASS_STATE_BEING_LINKED;
private static int CLASS_STATE_LINKED; private static int CLASS_STATE_LINKED;
private static int CLASS_STATE_BEING_INITIALIZED; private static int CLASS_STATE_BEING_INITIALIZED;
private static int CLASS_STATE_FULLY_INITIALIZED; private static int CLASS_STATE_FULLY_INITIALIZED;
@ -118,6 +119,7 @@ public class InstanceKlass extends Klass {
// read ClassState constants // read ClassState constants
CLASS_STATE_ALLOCATED = db.lookupIntConstant("InstanceKlass::allocated").intValue(); CLASS_STATE_ALLOCATED = db.lookupIntConstant("InstanceKlass::allocated").intValue();
CLASS_STATE_LOADED = db.lookupIntConstant("InstanceKlass::loaded").intValue(); CLASS_STATE_LOADED = db.lookupIntConstant("InstanceKlass::loaded").intValue();
CLASS_STATE_BEING_LINKED = db.lookupIntConstant("InstanceKlass::being_linked").intValue();
CLASS_STATE_LINKED = db.lookupIntConstant("InstanceKlass::linked").intValue(); CLASS_STATE_LINKED = db.lookupIntConstant("InstanceKlass::linked").intValue();
CLASS_STATE_BEING_INITIALIZED = db.lookupIntConstant("InstanceKlass::being_initialized").intValue(); CLASS_STATE_BEING_INITIALIZED = db.lookupIntConstant("InstanceKlass::being_initialized").intValue();
CLASS_STATE_FULLY_INITIALIZED = db.lookupIntConstant("InstanceKlass::fully_initialized").intValue(); CLASS_STATE_FULLY_INITIALIZED = db.lookupIntConstant("InstanceKlass::fully_initialized").intValue();
@ -184,6 +186,7 @@ public class InstanceKlass extends Klass {
public static class ClassState { public static class ClassState {
public static final ClassState ALLOCATED = new ClassState("allocated"); public static final ClassState ALLOCATED = new ClassState("allocated");
public static final ClassState LOADED = new ClassState("loaded"); public static final ClassState LOADED = new ClassState("loaded");
public static final ClassState BEING_LINKED = new ClassState("beingLinked");
public static final ClassState LINKED = new ClassState("linked"); public static final ClassState LINKED = new ClassState("linked");
public static final ClassState BEING_INITIALIZED = new ClassState("beingInitialized"); public static final ClassState BEING_INITIALIZED = new ClassState("beingInitialized");
public static final ClassState FULLY_INITIALIZED = new ClassState("fullyInitialized"); public static final ClassState FULLY_INITIALIZED = new ClassState("fullyInitialized");
@ -207,6 +210,8 @@ public class InstanceKlass extends Klass {
return ClassState.ALLOCATED; return ClassState.ALLOCATED;
} else if (state == CLASS_STATE_LOADED) { } else if (state == CLASS_STATE_LOADED) {
return ClassState.LOADED; return ClassState.LOADED;
} else if (state == CLASS_STATE_BEING_LINKED) {
return ClassState.BEING_LINKED;
} else if (state == CLASS_STATE_LINKED) { } else if (state == CLASS_STATE_LINKED) {
return ClassState.LINKED; return ClassState.LINKED;
} else if (state == CLASS_STATE_BEING_INITIALIZED) { } else if (state == CLASS_STATE_BEING_INITIALIZED) {

View File

@ -97,7 +97,7 @@ class HotSpotVMConfig extends HotSpotVMConfigAccess {
final int vtableEntrySize = getFieldValue("CompilerToVM::Data::sizeof_vtableEntry", Integer.class, "int"); final int vtableEntrySize = getFieldValue("CompilerToVM::Data::sizeof_vtableEntry", Integer.class, "int");
final int vtableEntryMethodOffset = getFieldOffset("vtableEntry::_method", Integer.class, "Method*"); final int vtableEntryMethodOffset = getFieldOffset("vtableEntry::_method", Integer.class, "Method*");
final int instanceKlassInitStateOffset = getFieldOffset("InstanceKlass::_init_state", Integer.class, "u1"); final int instanceKlassInitStateOffset = getFieldOffset("InstanceKlass::_init_state", Integer.class, "InstanceKlass::ClassState");
final int instanceKlassConstantsOffset = getFieldOffset("InstanceKlass::_constants", Integer.class, "ConstantPool*"); final int instanceKlassConstantsOffset = getFieldOffset("InstanceKlass::_constants", Integer.class, "ConstantPool*");
final int instanceKlassFieldsOffset = getFieldOffset("InstanceKlass::_fields", Integer.class, "Array<u2>*"); final int instanceKlassFieldsOffset = getFieldOffset("InstanceKlass::_fields", Integer.class, "Array<u2>*");
final int instanceKlassAnnotationsOffset = getFieldOffset("InstanceKlass::_annotations", Integer.class, "Annotations*"); final int instanceKlassAnnotationsOffset = getFieldOffset("InstanceKlass::_annotations", Integer.class, "Annotations*");

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -60,7 +60,8 @@ public class TestThreadDumpClassInitMonitor {
*/ */
final static String TEST_THREAD = "TestThread"; final static String TEST_THREAD = "TestThread";
final static String TEST_THREAD_ENTRY = "\"" + TEST_THREAD; final static String TEST_THREAD_ENTRY = "\"" + TEST_THREAD;
final static String IN_OBJECT_WAIT = "in Object.wait()"; // final static String IN_OBJECT_WAIT = "in Object.wait()";
final static String IN_CONVAR_WAIT = "waiting on condition";
final static String THREAD_STATE = "java.lang.Thread.State: RUNNABLE"; final static String THREAD_STATE = "java.lang.Thread.State: RUNNABLE";
final static String THREAD_INFO = "Thread:"; // the details are not important final static String THREAD_INFO = "Thread:"; // the details are not important
final static String JAVATHREAD_STATE = "JavaThread state: _thread_blocked"; final static String JAVATHREAD_STATE = "JavaThread state: _thread_blocked";
@ -139,7 +140,7 @@ public class TestThreadDumpClassInitMonitor {
continue; continue;
} }
foundLines++; foundLines++;
if (!line.contains(IN_OBJECT_WAIT)) { if (!line.contains(IN_CONVAR_WAIT)) {
throw new Error("Unexpected initial stack line: " + line); throw new Error("Unexpected initial stack line: " + line);
} }
continue; continue;