8288064: Class initialization locking
Reviewed-by: rehn, vlivanov
This commit is contained in:
parent
3d12c0225b
commit
cf4a4966a8
@ -792,7 +792,6 @@ int java_lang_Class::_class_loader_offset;
|
||||
int java_lang_Class::_module_offset;
|
||||
int java_lang_Class::_protection_domain_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::_name_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 classData,
|
||||
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(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
|
||||
InstanceKlass::cast(k)->do_local_static_fields(&reset);
|
||||
|
||||
set_init_lock(archived_mirror, NULL);
|
||||
|
||||
set_protection_domain(archived_mirror, NULL);
|
||||
set_signers(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()) {
|
||||
// - 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()) {
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
assert(_signers_offset != 0, "must be set");
|
||||
return (objArrayOop)java_class->obj_field(_signers_offset);
|
||||
@ -1641,18 +1619,12 @@ void java_lang_Class::compute_offsets() {
|
||||
InstanceKlass* k = vmClasses::Class_klass();
|
||||
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);
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS
|
||||
void java_lang_Class::serialize_offsets(SerializeClosure* f) {
|
||||
f->do_bool(&_offsets_computed);
|
||||
f->do_u4((u4*)&_init_lock_offset);
|
||||
|
||||
CLASS_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
|
||||
|
||||
|
@ -285,7 +285,6 @@ class java_lang_Class : AllStatic {
|
||||
static int _static_oop_field_count_offset;
|
||||
|
||||
static int _protection_domain_offset;
|
||||
static int _init_lock_offset;
|
||||
static int _signers_offset;
|
||||
static int _class_loader_offset;
|
||||
static int _module_offset;
|
||||
@ -300,7 +299,6 @@ class java_lang_Class : AllStatic {
|
||||
static GrowableArray<Klass*>* _fixup_mirror_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_class_loader(oop java_class, oop class_loader);
|
||||
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
|
||||
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 objArrayOop signers(oop java_class);
|
||||
static void set_signers(oop java_class, objArrayOop signers);
|
||||
|
@ -1774,7 +1774,7 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
|
||||
// the interpreter or runtime performs a serialized check of
|
||||
// the relevant CPCE::f1 field. This is done by the caller
|
||||
// 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.
|
||||
|
||||
// Log dynamic info to CDS classlist.
|
||||
|
@ -156,7 +156,7 @@
|
||||
\
|
||||
nonstatic_field(InstanceKlass, _fields, Array<u2>*) \
|
||||
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, _misc_flags, u2) \
|
||||
nonstatic_field(InstanceKlass, _annotations, Annotations*) \
|
||||
|
@ -266,7 +266,7 @@ void ConstantPoolCacheEntry::set_direct_or_vtable_call(Bytecodes::Code invoke_co
|
||||
}
|
||||
if (invoke_code == Bytecodes::_invokestatic) {
|
||||
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");
|
||||
|
||||
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
|
||||
// the lock, so that when the losing writer returns, he can use the linked
|
||||
// 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()) {
|
||||
return;
|
||||
}
|
||||
@ -453,6 +447,7 @@ void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle&
|
||||
// Store appendix, if any.
|
||||
if (has_appendix) {
|
||||
const int appendix_index = f2_as_index();
|
||||
objArrayOop resolved_references = cpool->resolved_references();
|
||||
assert(appendix_index >= 0 && appendix_index < resolved_references->length(), "oob");
|
||||
assert(resolved_references->obj_at(appendix_index) == NULL, "init just once");
|
||||
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()),
|
||||
"No LinkageError exception");
|
||||
|
||||
// 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.
|
||||
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);
|
||||
MutexLocker ml(THREAD, cpool->pool_holder()->init_monitor());
|
||||
|
||||
// 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
|
||||
|
@ -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
|
||||
|
@ -143,9 +143,10 @@ class InstanceKlass: public Klass {
|
||||
|
||||
// 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.
|
||||
enum ClassState {
|
||||
enum ClassState : u1 {
|
||||
allocated, // allocated (but not yet linked)
|
||||
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)
|
||||
being_initialized, // currently running class initializer
|
||||
fully_initialized, // initialized (successful final state)
|
||||
@ -224,12 +225,9 @@ class InstanceKlass: public Klass {
|
||||
// _misc_flags.
|
||||
bool _is_marked_dependent; // used for marking during flushing and deoptimization
|
||||
|
||||
// Class states are defined as ClassState (see above).
|
||||
// Place the _init_state here to utilize the unused 2-byte after
|
||||
// _idnum_allocated_count.
|
||||
u1 _init_state; // state of class
|
||||
ClassState _init_state; // state of class
|
||||
|
||||
u1 _reference_type; // reference type
|
||||
u1 _reference_type; // reference type
|
||||
|
||||
enum {
|
||||
_misc_rewritten = 1 << 0, // methods rewritten.
|
||||
@ -252,7 +250,9 @@ class InstanceKlass: public Klass {
|
||||
}
|
||||
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)
|
||||
|
||||
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
|
||||
jmethodID* volatile _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum, or NULL if none
|
||||
@ -536,17 +536,33 @@ public:
|
||||
TRAPS);
|
||||
public:
|
||||
// initialization state
|
||||
bool is_loaded() const { return _init_state >= loaded; }
|
||||
bool is_linked() const { return _init_state >= linked; }
|
||||
bool is_initialized() const { return _init_state == fully_initialized; }
|
||||
bool is_not_initialized() const { return _init_state < being_initialized; }
|
||||
bool is_being_initialized() const { return _init_state == being_initialized; }
|
||||
bool is_in_error_state() const { return _init_state == initialization_error; }
|
||||
bool is_reentrant_initialization(Thread *thread) { return thread == _init_thread; }
|
||||
ClassState init_state() { return (ClassState)_init_state; }
|
||||
bool is_loaded() const { return init_state() >= loaded; }
|
||||
bool is_linked() const { return init_state() >= linked; }
|
||||
bool is_being_linked() const { return init_state() == being_linked; }
|
||||
bool is_initialized() const { return init_state() == fully_initialized; }
|
||||
bool is_not_initialized() const { return init_state() < being_initialized; }
|
||||
bool is_being_initialized() const { return init_state() == being_initialized; }
|
||||
bool is_in_error_state() const { return init_state() == initialization_error; }
|
||||
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;
|
||||
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
|
||||
bool is_sealed() const;
|
||||
|
||||
@ -911,7 +927,7 @@ public:
|
||||
|
||||
// initialization
|
||||
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* oop_map_cache() { return _oop_map_cache; }
|
||||
@ -1138,11 +1154,14 @@ public:
|
||||
public:
|
||||
u2 idnum_allocated_count() const { return _idnum_allocated_count; }
|
||||
|
||||
private:
|
||||
private:
|
||||
// initialization state
|
||||
void set_init_state(ClassState state);
|
||||
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
|
||||
// which will cause the caches to grow. Safety requires different
|
||||
@ -1154,12 +1173,6 @@ private:
|
||||
|
||||
// Lock during initialization
|
||||
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
|
||||
virtual Klass* array_klass(int n, TRAPS);
|
||||
virtual Klass* array_klass_or_null(int n);
|
||||
@ -1169,9 +1182,10 @@ public:
|
||||
virtual Klass* array_klass_or_null();
|
||||
|
||||
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 verify_code (TRAPS);
|
||||
void initialize_impl (TRAPS);
|
||||
|
@ -1405,7 +1405,7 @@ methodHandle SharedRuntime::resolve_sub_helper(bool is_virtual, bool is_optimize
|
||||
|
||||
if (invoke_code == Bytecodes::_invokestatic) {
|
||||
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");
|
||||
if (!VM_Version::supports_fast_class_init_checks() && callee_method->needs_clinit_barrier()) {
|
||||
// In order to keep class initialization check, do not patch call
|
||||
|
@ -226,9 +226,8 @@ void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) {
|
||||
Klass* k = obj->klass();
|
||||
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) {
|
||||
// We are waiting on an Object monitor but Object.wait() isn't the
|
||||
// top-frame, so we should be waiting on a Class initialization monitor.
|
||||
else if (thread()->osthread()->get_state() == CONDVAR_WAIT) {
|
||||
// We are waiting on the native class initialization monitor.
|
||||
InstanceKlass* k = thread()->class_to_be_initialized();
|
||||
if (k != NULL) {
|
||||
st->print_cr("\t- waiting on the Class initialization monitor for %s", k->external_name());
|
||||
|
@ -238,7 +238,7 @@
|
||||
nonstatic_field(InstanceKlass, _nonstatic_oop_map_size, int) \
|
||||
nonstatic_field(InstanceKlass, _is_marked_dependent, bool) \
|
||||
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, _itable_len, int) \
|
||||
nonstatic_field(InstanceKlass, _reference_type, u1) \
|
||||
@ -2280,6 +2280,7 @@
|
||||
\
|
||||
declare_constant(InstanceKlass::allocated) \
|
||||
declare_constant(InstanceKlass::loaded) \
|
||||
declare_constant(InstanceKlass::being_linked) \
|
||||
declare_constant(InstanceKlass::linked) \
|
||||
declare_constant(InstanceKlass::being_initialized) \
|
||||
declare_constant(InstanceKlass::fully_initialized) \
|
||||
|
@ -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.
|
||||
return size + field_count * (sizeof(address) + 1);
|
||||
}
|
||||
@ -1171,14 +1163,6 @@ void DumperSupport::dump_static_fields(AbstractDumpWriter* writer, Klass* k) {
|
||||
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
|
||||
|
@ -60,6 +60,7 @@ public class InstanceKlass extends Klass {
|
||||
// ClassState constants
|
||||
private static int CLASS_STATE_ALLOCATED;
|
||||
private static int CLASS_STATE_LOADED;
|
||||
private static int CLASS_STATE_BEING_LINKED;
|
||||
private static int CLASS_STATE_LINKED;
|
||||
private static int CLASS_STATE_BEING_INITIALIZED;
|
||||
private static int CLASS_STATE_FULLY_INITIALIZED;
|
||||
@ -118,6 +119,7 @@ public class InstanceKlass extends Klass {
|
||||
// read ClassState constants
|
||||
CLASS_STATE_ALLOCATED = db.lookupIntConstant("InstanceKlass::allocated").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_BEING_INITIALIZED = db.lookupIntConstant("InstanceKlass::being_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 final ClassState ALLOCATED = new ClassState("allocated");
|
||||
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 BEING_INITIALIZED = new ClassState("beingInitialized");
|
||||
public static final ClassState FULLY_INITIALIZED = new ClassState("fullyInitialized");
|
||||
@ -207,6 +210,8 @@ public class InstanceKlass extends Klass {
|
||||
return ClassState.ALLOCATED;
|
||||
} else if (state == CLASS_STATE_LOADED) {
|
||||
return ClassState.LOADED;
|
||||
} else if (state == CLASS_STATE_BEING_LINKED) {
|
||||
return ClassState.BEING_LINKED;
|
||||
} else if (state == CLASS_STATE_LINKED) {
|
||||
return ClassState.LINKED;
|
||||
} else if (state == CLASS_STATE_BEING_INITIALIZED) {
|
||||
|
@ -97,7 +97,7 @@ class HotSpotVMConfig extends HotSpotVMConfigAccess {
|
||||
final int vtableEntrySize = getFieldValue("CompilerToVM::Data::sizeof_vtableEntry", Integer.class, "int");
|
||||
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 instanceKlassFieldsOffset = getFieldOffset("InstanceKlass::_fields", Integer.class, "Array<u2>*");
|
||||
final int instanceKlassAnnotationsOffset = getFieldOffset("InstanceKlass::_annotations", Integer.class, "Annotations*");
|
||||
|
@ -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.
|
||||
*
|
||||
* 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_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_INFO = "Thread:"; // the details are not important
|
||||
final static String JAVATHREAD_STATE = "JavaThread state: _thread_blocked";
|
||||
@ -139,7 +140,7 @@ public class TestThreadDumpClassInitMonitor {
|
||||
continue;
|
||||
}
|
||||
foundLines++;
|
||||
if (!line.contains(IN_OBJECT_WAIT)) {
|
||||
if (!line.contains(IN_CONVAR_WAIT)) {
|
||||
throw new Error("Unexpected initial stack line: " + line);
|
||||
}
|
||||
continue;
|
||||
|
Loading…
x
Reference in New Issue
Block a user