8155951: VM crash in nsk/jvmti/RedefineClasses/StressRedefine: assert failed: Corrupted constant pool

8151066: assert(0 <= i && i < length()) failed: index out of bounds

Lock classes for redefinition because constant pool merging isn't thread safe, use method constant pool because constant pool merging doesn't make equivalent cpCaches because of invokedynamic

Reviewed-by: sspitsyn, dholmes
This commit is contained in:
Coleen Phillimore 2016-05-17 08:51:05 -04:00
parent e84506041e
commit 178842d251
7 changed files with 75 additions and 25 deletions

View file

@ -69,6 +69,43 @@ VM_RedefineClasses::VM_RedefineClasses(jint class_count,
_res = JVMTI_ERROR_NONE;
}
static inline InstanceKlass* get_ik(jclass def) {
oop mirror = JNIHandles::resolve_non_null(def);
return InstanceKlass::cast(java_lang_Class::as_Klass(mirror));
}
// If any of the classes are being redefined, wait
// Parallel constant pool merging leads to indeterminate constant pools.
void VM_RedefineClasses::lock_classes() {
MutexLocker ml(RedefineClasses_lock);
bool has_redefined;
do {
has_redefined = false;
// Go through classes each time until none are being redefined.
for (int i = 0; i < _class_count; i++) {
if (get_ik(_class_defs[i].klass)->is_being_redefined()) {
RedefineClasses_lock->wait();
has_redefined = true;
break; // for loop
}
}
} while (has_redefined);
for (int i = 0; i < _class_count; i++) {
get_ik(_class_defs[i].klass)->set_is_being_redefined(true);
}
RedefineClasses_lock->notify_all();
}
void VM_RedefineClasses::unlock_classes() {
MutexLocker ml(RedefineClasses_lock);
for (int i = 0; i < _class_count; i++) {
assert(get_ik(_class_defs[i].klass)->is_being_redefined(),
"should be being redefined to get here");
get_ik(_class_defs[i].klass)->set_is_being_redefined(false);
}
RedefineClasses_lock->notify_all();
}
bool VM_RedefineClasses::doit_prologue() {
if (_class_count == 0) {
_res = JVMTI_ERROR_NONE;
@ -91,6 +128,14 @@ bool VM_RedefineClasses::doit_prologue() {
_res = JVMTI_ERROR_NULL_POINTER;
return false;
}
oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass);
// classes for primitives and arrays cannot be redefined
// check here so following code can assume these classes are InstanceKlass
if (!is_modifiable_class(mirror)) {
_res = JVMTI_ERROR_UNMODIFIABLE_CLASS;
return false;
}
}
// Start timer after all the sanity checks; not quite accurate, but
@ -99,6 +144,7 @@ bool VM_RedefineClasses::doit_prologue() {
_timer_vm_op_prologue.start();
}
lock_classes();
// We first load new class versions in the prologue, because somewhere down the
// call chain it is required that the current thread is a Java thread.
_res = load_new_class_versions(Thread::current());
@ -115,6 +161,7 @@ bool VM_RedefineClasses::doit_prologue() {
// Free os::malloc allocated memory in load_new_class_version.
os::free(_scratch_classes);
_timer_vm_op_prologue.stop();
unlock_classes();
return false;
}
@ -174,6 +221,8 @@ void VM_RedefineClasses::doit() {
}
void VM_RedefineClasses::doit_epilogue() {
unlock_classes();
// Free os::malloc allocated memory.
os::free(_scratch_classes);
@ -959,14 +1008,7 @@ jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) {
// versions are deleted. Constant pools are deallocated while merging
// constant pools
HandleMark hm(THREAD);
oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass);
// classes for primitives cannot be redefined
if (!is_modifiable_class(mirror)) {
return JVMTI_ERROR_UNMODIFIABLE_CLASS;
}
Klass* the_class_oop = java_lang_Class::as_Klass(mirror);
instanceKlassHandle the_class = instanceKlassHandle(THREAD, the_class_oop);
instanceKlassHandle the_class(THREAD, get_ik(_class_defs[i].klass));
Symbol* the_class_sym = the_class->name();
log_debug(redefine, class, load)
@ -3757,22 +3799,19 @@ void VM_RedefineClasses::redefine_single_class(jclass the_jclass,
_timer_rsc_phase1.start();
}
instanceKlassHandle scratch_class(scratch_class_oop);
oop the_class_mirror = JNIHandles::resolve_non_null(the_jclass);
Klass* the_class_oop = java_lang_Class::as_Klass(the_class_mirror);
instanceKlassHandle the_class = instanceKlassHandle(THREAD, the_class_oop);
instanceKlassHandle scratch_class(THREAD, scratch_class_oop);
instanceKlassHandle the_class(THREAD, get_ik(the_jclass));
// Remove all breakpoints in methods of this class
JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints();
jvmti_breakpoints.clearall_in_class_at_safepoint(the_class_oop);
jvmti_breakpoints.clearall_in_class_at_safepoint(the_class());
// Deoptimize all compiled code that depends on this class
flush_dependent_code(the_class, THREAD);
_old_methods = the_class->methods();
_new_methods = scratch_class->methods();
_the_class_oop = the_class_oop;
_the_class_oop = the_class();
compute_added_deleted_matching_methods();
update_jmethod_ids();
@ -4002,10 +4041,10 @@ void VM_RedefineClasses::redefine_single_class(jclass the_jclass,
increment_class_counter((InstanceKlass *)the_class(), THREAD);
log_info(redefine, class, load)
("redefined name=%s, count=%d (avail_mem=" UINT64_FORMAT "K)",
the_class->external_name(), java_lang_Class::classRedefinedCount(the_class_mirror), os::available_memory() >> 10);
the_class->external_name(), java_lang_Class::classRedefinedCount(the_class->java_mirror()), os::available_memory() >> 10);
Events::log_redefinition(THREAD, "redefined class name=%s, count=%d",
the_class->external_name(),
java_lang_Class::classRedefinedCount(the_class_mirror));
java_lang_Class::classRedefinedCount(the_class->java_mirror()));
}
_timer_rsc_phase2.stop();