8008962: NPG: Memory regression: One extra Monitor per ConstantPool
Re-use InstanceKlass::_init_lock locking ConstantPool as well. Reviewed-by: dholmes, coleenp, acorn
This commit is contained in:
parent
333cf9a756
commit
4e19360f89
@ -483,7 +483,8 @@ ciKlass* ciEnv::get_klass_by_index_impl(constantPoolHandle cpool,
|
||||
{
|
||||
// We have to lock the cpool to keep the oop from being resolved
|
||||
// while we are accessing it.
|
||||
MonitorLockerEx ml(cpool->lock());
|
||||
oop cplock = cpool->lock();
|
||||
ObjectLocker ol(cplock, THREAD, cplock != NULL);
|
||||
constantTag tag = cpool->tag_at(index);
|
||||
if (tag.is_klass()) {
|
||||
// The klass has been inserted into the constant pool
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "runtime/init.hpp"
|
||||
#include "runtime/javaCalls.hpp"
|
||||
#include "runtime/signature.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/vframe.hpp"
|
||||
|
||||
ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, TRAPS) {
|
||||
@ -69,7 +70,6 @@ ConstantPool::ConstantPool(Array<u1>* tags) {
|
||||
|
||||
// only set to non-zero if constant pool is merged by RedefineClasses
|
||||
set_version(0);
|
||||
set_lock(new Monitor(Monitor::nonleaf + 2, "A constant pool lock"));
|
||||
|
||||
// initialize tag array
|
||||
int length = tags->length();
|
||||
@ -95,9 +95,6 @@ void ConstantPool::deallocate_contents(ClassLoaderData* loader_data) {
|
||||
void ConstantPool::release_C_heap_structures() {
|
||||
// walk constant pool and decrement symbol reference counts
|
||||
unreference_symbols();
|
||||
|
||||
delete _lock;
|
||||
set_lock(NULL);
|
||||
}
|
||||
|
||||
objArrayOop ConstantPool::resolved_references() const {
|
||||
@ -154,9 +151,6 @@ void ConstantPool::restore_unshareable_info(TRAPS) {
|
||||
ClassLoaderData* loader_data = pool_holder()->class_loader_data();
|
||||
set_resolved_references(loader_data->add_handle(refs_handle));
|
||||
}
|
||||
|
||||
// Also need to recreate the mutex. Make sure this matches the constructor
|
||||
set_lock(new Monitor(Monitor::nonleaf + 2, "A constant pool lock"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +161,23 @@ void ConstantPool::remove_unshareable_info() {
|
||||
set_resolved_reference_length(
|
||||
resolved_references() != NULL ? resolved_references()->length() : 0);
|
||||
set_resolved_references(NULL);
|
||||
set_lock(NULL);
|
||||
}
|
||||
|
||||
oop ConstantPool::lock() {
|
||||
if (_pool_holder) {
|
||||
// We re-use the _pool_holder's init_lock to reduce footprint.
|
||||
// Notes on deadlocks:
|
||||
// [1] This lock is a Java oop, so it can be recursively locked by
|
||||
// the same thread without self-deadlocks.
|
||||
// [2] Deadlock will happen if there is circular dependency between
|
||||
// the <clinit> of two Java classes. However, in this case,
|
||||
// the deadlock would have happened long before we reach
|
||||
// ConstantPool::lock(), so reusing init_lock does not
|
||||
// increase the possibility of deadlock.
|
||||
return _pool_holder->init_lock();
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int ConstantPool::cp_to_object_index(int cp_index) {
|
||||
@ -208,7 +218,9 @@ Klass* ConstantPool::klass_at_impl(constantPoolHandle this_oop, int which, TRAPS
|
||||
|
||||
Symbol* name = NULL;
|
||||
Handle loader;
|
||||
{ MonitorLockerEx ml(this_oop->lock());
|
||||
{
|
||||
oop cplock = this_oop->lock();
|
||||
ObjectLocker ol(cplock , THREAD, cplock != NULL);
|
||||
|
||||
if (this_oop->tag_at(which).is_unresolved_klass()) {
|
||||
if (this_oop->tag_at(which).is_unresolved_klass_in_error()) {
|
||||
@ -255,7 +267,8 @@ Klass* ConstantPool::klass_at_impl(constantPoolHandle this_oop, int which, TRAPS
|
||||
|
||||
bool throw_orig_error = false;
|
||||
{
|
||||
MonitorLockerEx ml(this_oop->lock());
|
||||
oop cplock = this_oop->lock();
|
||||
ObjectLocker ol(cplock, THREAD, cplock != NULL);
|
||||
|
||||
// some other thread has beaten us and has resolved the class.
|
||||
if (this_oop->tag_at(which).is_klass()) {
|
||||
@ -323,7 +336,8 @@ Klass* ConstantPool::klass_at_impl(constantPoolHandle this_oop, int which, TRAPS
|
||||
}
|
||||
return k();
|
||||
} else {
|
||||
MonitorLockerEx ml(this_oop->lock());
|
||||
oop cplock = this_oop->lock();
|
||||
ObjectLocker ol(cplock, THREAD, cplock != NULL);
|
||||
// Only updated constant pool - if it is resolved.
|
||||
do_resolve = this_oop->tag_at(which).is_unresolved_klass();
|
||||
if (do_resolve) {
|
||||
@ -619,7 +633,8 @@ void ConstantPool::save_and_throw_exception(constantPoolHandle this_oop, int whi
|
||||
int tag, TRAPS) {
|
||||
ResourceMark rm;
|
||||
Symbol* error = PENDING_EXCEPTION->klass()->name();
|
||||
MonitorLockerEx ml(this_oop->lock()); // lock cpool to change tag.
|
||||
oop cplock = this_oop->lock();
|
||||
ObjectLocker ol(cplock, THREAD, cplock != NULL); // lock cpool to change tag.
|
||||
|
||||
int error_tag = (tag == JVM_CONSTANT_MethodHandle) ?
|
||||
JVM_CONSTANT_MethodHandleInError : JVM_CONSTANT_MethodTypeInError;
|
||||
@ -780,7 +795,8 @@ oop ConstantPool::resolve_constant_at_impl(constantPoolHandle this_oop, int inde
|
||||
if (cache_index >= 0) {
|
||||
// Cache the oop here also.
|
||||
Handle result_handle(THREAD, result_oop);
|
||||
MonitorLockerEx ml(this_oop->lock()); // don't know if we really need this
|
||||
oop cplock = this_oop->lock();
|
||||
ObjectLocker ol(cplock, THREAD, cplock != NULL); // don't know if we really need this
|
||||
oop result = this_oop->resolved_references()->obj_at(cache_index);
|
||||
// Benign race condition: resolved_references may already be filled in while we were trying to lock.
|
||||
// The important thing here is that all threads pick up the same result.
|
||||
|
@ -111,7 +111,6 @@ class ConstantPool : public Metadata {
|
||||
int _version;
|
||||
} _saved;
|
||||
|
||||
Monitor* _lock;
|
||||
|
||||
void set_tags(Array<u1>* tags) { _tags = tags; }
|
||||
void tag_at_put(int which, jbyte t) { tags()->at_put(which, t); }
|
||||
@ -823,8 +822,17 @@ class ConstantPool : public Metadata {
|
||||
|
||||
void set_resolved_reference_length(int length) { _saved._resolved_reference_length = length; }
|
||||
int resolved_reference_length() const { return _saved._resolved_reference_length; }
|
||||
void set_lock(Monitor* lock) { _lock = lock; }
|
||||
Monitor* lock() { return _lock; }
|
||||
|
||||
// lock() may return null -- constant pool updates may happen before this lock is
|
||||
// initialized, because the _pool_holder has not been fully initialized and
|
||||
// has not been registered into the system dictionary. In this case, no other
|
||||
// thread can be modifying this constantpool, so no synchronization is
|
||||
// necessary.
|
||||
//
|
||||
// Use cplock() like this:
|
||||
// oop cplock = cp->lock();
|
||||
// ObjectLocker ol(cplock , THREAD, cplock != NULL);
|
||||
oop lock();
|
||||
|
||||
// Decrease ref counts of symbols that are in the constant pool
|
||||
// when the holder class is unloaded
|
||||
|
@ -266,7 +266,8 @@ void ConstantPoolCacheEntry::set_method_handle_common(constantPoolHandle cpool,
|
||||
// the lock, so that when the losing writer returns, he can use the linked
|
||||
// cache entry.
|
||||
|
||||
MonitorLockerEx ml(cpool->lock());
|
||||
oop cplock = cpool->lock();
|
||||
ObjectLocker ol(cplock, Thread::current(), cplock != NULL);
|
||||
if (!is_f1_null()) {
|
||||
return;
|
||||
}
|
||||
|
@ -419,25 +419,6 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
|
||||
set_annotations(NULL);
|
||||
}
|
||||
|
||||
volatile oop InstanceKlass::init_lock() const {
|
||||
volatile oop lock = _init_lock; // read once
|
||||
assert((oop)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();
|
||||
klass_oop_store(&_init_lock, NULL);
|
||||
assert(!is_not_initialized(), "class must be initialized now");
|
||||
}
|
||||
|
||||
|
||||
bool InstanceKlass::should_be_initialized() const {
|
||||
return !is_initialized();
|
||||
}
|
||||
@ -474,7 +455,7 @@ void InstanceKlass::eager_initialize(Thread *thread) {
|
||||
void InstanceKlass::eager_initialize_impl(instanceKlassHandle this_oop) {
|
||||
EXCEPTION_MARK;
|
||||
volatile oop init_lock = this_oop->init_lock();
|
||||
ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
|
||||
ObjectLocker ol(init_lock, THREAD);
|
||||
|
||||
// abort if someone beat us to the initialization
|
||||
if (!this_oop->is_not_initialized()) return; // note: not equivalent to is_initialized()
|
||||
@ -493,7 +474,6 @@ void InstanceKlass::eager_initialize_impl(instanceKlassHandle this_oop) {
|
||||
} else {
|
||||
// linking successfull, mark class as initialized
|
||||
this_oop->set_init_state (fully_initialized);
|
||||
this_oop->fence_and_clear_init_lock();
|
||||
// trace
|
||||
if (TraceClassInitialization) {
|
||||
ResourceMark rm(THREAD);
|
||||
@ -620,7 +600,7 @@ bool InstanceKlass::link_class_impl(
|
||||
// verification & rewriting
|
||||
{
|
||||
volatile oop init_lock = this_oop->init_lock();
|
||||
ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
|
||||
ObjectLocker ol(init_lock, THREAD);
|
||||
// rewritten will have been set if loader constraint error found
|
||||
// on an earlier link attempt
|
||||
// don't verify or rewrite if already rewritten
|
||||
@ -743,7 +723,7 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) {
|
||||
// Step 1
|
||||
{
|
||||
volatile oop init_lock = this_oop->init_lock();
|
||||
ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
|
||||
ObjectLocker ol(init_lock, THREAD);
|
||||
|
||||
Thread *self = THREAD; // it's passed the current thread
|
||||
|
||||
@ -891,9 +871,8 @@ void InstanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS)
|
||||
|
||||
void InstanceKlass::set_initialization_state_and_notify_impl(instanceKlassHandle this_oop, ClassState state, TRAPS) {
|
||||
volatile oop init_lock = this_oop->init_lock();
|
||||
ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
|
||||
ObjectLocker ol(init_lock, THREAD);
|
||||
this_oop->set_init_state(state);
|
||||
this_oop->fence_and_clear_init_lock();
|
||||
ol.notify_all(CHECK);
|
||||
}
|
||||
|
||||
@ -2860,7 +2839,7 @@ void InstanceKlass::print_on(outputStream* st) const {
|
||||
st->print(BULLET"protection domain: "); ((InstanceKlass*)this)->protection_domain()->print_value_on(st); st->cr();
|
||||
st->print(BULLET"host class: "); host_klass()->print_value_on_maybe_null(st); st->cr();
|
||||
st->print(BULLET"signers: "); signers()->print_value_on(st); st->cr();
|
||||
st->print(BULLET"init_lock: "); ((oop)_init_lock)->print_value_on(st); st->cr();
|
||||
st->print(BULLET"init_lock: "); ((oop)_init_lock)->print_value_on(st); st->cr();
|
||||
if (source_file_name() != NULL) {
|
||||
st->print(BULLET"source file: ");
|
||||
source_file_name()->print_value_on(st);
|
||||
|
@ -184,8 +184,9 @@ class InstanceKlass: public Klass {
|
||||
oop _protection_domain;
|
||||
// Class signers.
|
||||
objArrayOop _signers;
|
||||
// Initialization lock. Must be one per class and it has to be a VM internal
|
||||
// object so java code cannot lock it (like the mirror)
|
||||
// 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.
|
||||
volatile oop _init_lock;
|
||||
|
||||
@ -970,6 +971,7 @@ class InstanceKlass: public Klass {
|
||||
#endif // INCLUDE_ALL_GCS
|
||||
|
||||
u2 idnum_allocated_count() const { return _idnum_allocated_count; }
|
||||
|
||||
private:
|
||||
// initialization state
|
||||
#ifdef ASSERT
|
||||
@ -996,9 +998,10 @@ private:
|
||||
{ OrderAccess::release_store_ptr(&_methods_cached_itable_indices, indices); }
|
||||
|
||||
// Lock during initialization
|
||||
volatile oop init_lock() const;
|
||||
void set_init_lock(oop value) { klass_oop_store(&_init_lock, value); }
|
||||
void fence_and_clear_init_lock(); // after fully_initialized
|
||||
public:
|
||||
volatile oop init_lock() const {return _init_lock; }
|
||||
private:
|
||||
void set_init_lock(oop value) { klass_oop_store(&_init_lock, value); }
|
||||
|
||||
// Offsets for memory management
|
||||
oop* adr_protection_domain() const { return (oop*)&this->_protection_domain;}
|
||||
|
@ -259,7 +259,8 @@ JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) {
|
||||
// bytes to the InstanceKlass here because they have not been
|
||||
// validated and we're not at a safepoint.
|
||||
constantPoolHandle constants(current_thread, ikh->constants());
|
||||
MonitorLockerEx ml(constants->lock()); // lock constant pool while we query it
|
||||
oop cplock = constants->lock();
|
||||
ObjectLocker ol(cplock, current_thread, cplock != NULL); // lock constant pool while we query it
|
||||
|
||||
JvmtiClassFileReconstituter reconstituter(ikh);
|
||||
if (reconstituter.get_error() != JVMTI_ERROR_NONE) {
|
||||
@ -2417,7 +2418,8 @@ JvmtiEnv::GetConstantPool(oop k_mirror, jint* constant_pool_count_ptr, jint* con
|
||||
|
||||
instanceKlassHandle ikh(thread, k_oop);
|
||||
constantPoolHandle constants(thread, ikh->constants());
|
||||
MonitorLockerEx ml(constants->lock()); // lock constant pool while we query it
|
||||
oop cplock = constants->lock();
|
||||
ObjectLocker ol(cplock, thread, cplock != NULL); // lock constant pool while we query it
|
||||
|
||||
JvmtiConstantPoolReconstituter reconstituter(ikh);
|
||||
if (reconstituter.get_error() != JVMTI_ERROR_NONE) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user