8165246: [REDO] InstanceKlass::_previous_version_count goes negative

Make _has_previous_version a boolean that is set to true when previous version of a class is added or during class unloading call to purge_previous_versions

Reviewed-by: gtriantafill, dcubed, sspitsyn
This commit is contained in:
Coleen Phillimore 2016-09-07 15:25:21 -04:00
parent be0d2c98ed
commit 3c2621dbdd
4 changed files with 235 additions and 80 deletions

View file

@ -3365,88 +3365,119 @@ void InstanceKlass::set_init_state(ClassState state) {
#if INCLUDE_JVMTI
// RedefineClasses() support for previous versions:
int InstanceKlass::_previous_version_count = 0;
// RedefineClasses() support for previous versions
// Purge previous versions before adding new previous versions of the class.
void InstanceKlass::purge_previous_versions(InstanceKlass* ik) {
if (ik->previous_versions() != NULL) {
// This klass has previous versions so see what we can cleanup
// while it is safe to do so.
// Globally, there is at least one previous version of a class to walk
// during class unloading, which is saved because old methods in the class
// are still running. Otherwise the previous version list is cleaned up.
bool InstanceKlass::_has_previous_versions = false;
int deleted_count = 0; // leave debugging breadcrumbs
int live_count = 0;
ClassLoaderData* loader_data = ik->class_loader_data();
assert(loader_data != NULL, "should never be null");
// Returns true if there are previous versions of a class for class
// unloading only. Also resets the flag to false. purge_previous_version
// will set the flag to true if there are any left, i.e., if there's any
// work to do for next time. This is to avoid the expensive code cache
// walk in CLDG::do_unloading().
bool InstanceKlass::has_previous_versions_and_reset() {
bool ret = _has_previous_versions;
log_trace(redefine, class, iklass, purge)("Class unloading: has_previous_versions = %s",
ret ? "true" : "false");
_has_previous_versions = false;
return ret;
}
ResourceMark rm;
log_trace(redefine, class, iklass, purge)("%s: previous versions", ik->external_name());
// Purge previous versions before adding new previous versions of the class and
// during class unloading.
void InstanceKlass::purge_previous_version_list() {
assert(SafepointSynchronize::is_at_safepoint(), "only called at safepoint");
assert(has_been_redefined(), "Should only be called for main class");
// previous versions are linked together through the InstanceKlass
InstanceKlass* pv_node = ik->previous_versions();
InstanceKlass* last = ik;
int version = 0;
// Quick exit.
if (previous_versions() == NULL) {
return;
}
// check the previous versions list
for (; pv_node != NULL; ) {
// This klass has previous versions so see what we can cleanup
// while it is safe to do so.
ConstantPool* pvcp = pv_node->constants();
assert(pvcp != NULL, "cp ref was unexpectedly cleared");
int deleted_count = 0; // leave debugging breadcrumbs
int live_count = 0;
ClassLoaderData* loader_data = class_loader_data();
assert(loader_data != NULL, "should never be null");
if (!pvcp->on_stack()) {
// If the constant pool isn't on stack, none of the methods
// are executing. Unlink this previous_version.
// The previous version InstanceKlass is on the ClassLoaderData deallocate list
// so will be deallocated during the next phase of class unloading.
log_trace(redefine, class, iklass, purge)("previous version " INTPTR_FORMAT " is dead", p2i(pv_node));
// For debugging purposes.
pv_node->set_is_scratch_class();
pv_node->class_loader_data()->add_to_deallocate_list(pv_node);
pv_node = pv_node->previous_versions();
last->link_previous_versions(pv_node);
deleted_count++;
version++;
continue;
} else {
log_trace(redefine, class, iklass, purge)("previous version " INTPTR_FORMAT " is alive", p2i(pv_node));
assert(pvcp->pool_holder() != NULL, "Constant pool with no holder");
guarantee (!loader_data->is_unloading(), "unloaded classes can't be on the stack");
live_count++;
}
ResourceMark rm;
log_trace(redefine, class, iklass, purge)("%s: previous versions", external_name());
// At least one method is live in this previous version.
// Reset dead EMCP methods not to get breakpoints.
// All methods are deallocated when all of the methods for this class are no
// longer running.
Array<Method*>* method_refs = pv_node->methods();
if (method_refs != NULL) {
log_trace(redefine, class, iklass, purge)("previous methods length=%d", method_refs->length());
for (int j = 0; j < method_refs->length(); j++) {
Method* method = method_refs->at(j);
// previous versions are linked together through the InstanceKlass
InstanceKlass* pv_node = previous_versions();
InstanceKlass* last = this;
int version = 0;
if (!method->on_stack()) {
// no breakpoints for non-running methods
if (method->is_running_emcp()) {
method->set_running_emcp(false);
}
} else {
assert (method->is_obsolete() || method->is_running_emcp(),
"emcp method cannot run after emcp bit is cleared");
log_trace(redefine, class, iklass, purge)
("purge: %s(%s): prev method @%d in version @%d is alive",
method->name()->as_C_string(), method->signature()->as_C_string(), j, version);
// check the previous versions list
for (; pv_node != NULL; ) {
ConstantPool* pvcp = pv_node->constants();
assert(pvcp != NULL, "cp ref was unexpectedly cleared");
if (!pvcp->on_stack()) {
// If the constant pool isn't on stack, none of the methods
// are executing. Unlink this previous_version.
// The previous version InstanceKlass is on the ClassLoaderData deallocate list
// so will be deallocated during the next phase of class unloading.
log_trace(redefine, class, iklass, purge)
("previous version " INTPTR_FORMAT " is dead.", p2i(pv_node));
// For debugging purposes.
pv_node->set_is_scratch_class();
// Unlink from previous version list.
assert(pv_node->class_loader_data() == loader_data, "wrong loader_data");
InstanceKlass* next = pv_node->previous_versions();
pv_node->link_previous_versions(NULL); // point next to NULL
last->link_previous_versions(next);
// Add to the deallocate list after unlinking
loader_data->add_to_deallocate_list(pv_node);
pv_node = next;
deleted_count++;
version++;
continue;
} else {
log_trace(redefine, class, iklass, purge)("previous version " INTPTR_FORMAT " is alive", p2i(pv_node));
assert(pvcp->pool_holder() != NULL, "Constant pool with no holder");
guarantee (!loader_data->is_unloading(), "unloaded classes can't be on the stack");
live_count++;
// found a previous version for next time we do class unloading
_has_previous_versions = true;
}
// At least one method is live in this previous version.
// Reset dead EMCP methods not to get breakpoints.
// All methods are deallocated when all of the methods for this class are no
// longer running.
Array<Method*>* method_refs = pv_node->methods();
if (method_refs != NULL) {
log_trace(redefine, class, iklass, purge)("previous methods length=%d", method_refs->length());
for (int j = 0; j < method_refs->length(); j++) {
Method* method = method_refs->at(j);
if (!method->on_stack()) {
// no breakpoints for non-running methods
if (method->is_running_emcp()) {
method->set_running_emcp(false);
}
} else {
assert (method->is_obsolete() || method->is_running_emcp(),
"emcp method cannot run after emcp bit is cleared");
log_trace(redefine, class, iklass, purge)
("purge: %s(%s): prev method @%d in version @%d is alive",
method->name()->as_C_string(), method->signature()->as_C_string(), j, version);
}
}
// next previous version
last = pv_node;
pv_node = pv_node->previous_versions();
version++;
}
log_trace(redefine, class, iklass, purge)
("previous version stats: live=%d, deleted=%d",
live_count, deleted_count);
// next previous version
last = pv_node;
pv_node = pv_node->previous_versions();
version++;
}
log_trace(redefine, class, iklass, purge)
("previous version stats: live=%d, deleted=%d", live_count, deleted_count);
}
void InstanceKlass::mark_newly_obsolete_methods(Array<Method*>* old_methods,
@ -3518,8 +3549,8 @@ void InstanceKlass::add_previous_version(instanceKlassHandle scratch_class,
log_trace(redefine, class, iklass, add)
("adding previous version ref for %s, EMCP_cnt=%d", scratch_class->external_name(), emcp_method_count);
// Clean out old previous versions
purge_previous_versions(this);
// Clean out old previous versions for this class
purge_previous_version_list();
// Mark newly obsolete methods in remaining previous versions. An EMCP method from
// a previous redefinition may be made obsolete by this redefinition.
@ -3536,8 +3567,6 @@ void InstanceKlass::add_previous_version(instanceKlassHandle scratch_class,
// For debugging purposes.
scratch_class->set_is_scratch_class();
scratch_class->class_loader_data()->add_to_deallocate_list(scratch_class());
// Update count for class unloading.
_previous_version_count--;
return;
}
@ -3565,12 +3594,12 @@ void InstanceKlass::add_previous_version(instanceKlassHandle scratch_class,
}
// Add previous version if any methods are still running.
log_trace(redefine, class, iklass, add)("scratch class added; one of its methods is on_stack");
// Set has_previous_version flag for processing during class unloading.
_has_previous_versions = true;
log_trace(redefine, class, iklass, add) ("scratch class added; one of its methods is on_stack.");
assert(scratch_class->previous_versions() == NULL, "shouldn't have a previous version");
scratch_class->link_previous_versions(previous_versions());
link_previous_versions(scratch_class());
// Update count for class unloading.
_previous_version_count++;
} // end add_previous_version()
#endif // INCLUDE_JVMTI