1110 lines
45 KiB
Diff
1110 lines
45 KiB
Diff
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/classfile/metadataOnStackMark.cpp 2018-09-18 09:10:03.879505910 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/classfile/metadataOnStackMark.cpp 2018-09-18 09:11:09.647849381 +0200
|
|
@@ -41,13 +41,13 @@
|
|
// Walk metadata on the stack and mark it so that redefinition doesn't delete
|
|
// it. Class unloading also walks the previous versions and might try to
|
|
// delete it, so this class is used by class unloading also.
|
|
-MetadataOnStackMark::MetadataOnStackMark(bool visit_code_cache) {
|
|
+MetadataOnStackMark::MetadataOnStackMark(bool has_redefined_a_class) {
|
|
assert(SafepointSynchronize::is_at_safepoint(), "sanity check");
|
|
assert(_used_buffers == NULL, "sanity check");
|
|
NOT_PRODUCT(_is_active = true;)
|
|
|
|
Threads::metadata_do(Metadata::mark_on_stack);
|
|
- if (visit_code_cache) {
|
|
+ if (has_redefined_a_class) {
|
|
CodeCache::alive_nmethods_do(nmethod::mark_on_stack);
|
|
}
|
|
CompileBroker::mark_on_stack();
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/classfile/metadataOnStackMark.hpp 2018-09-18 09:10:03.879505910 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/classfile/metadataOnStackMark.hpp 2018-09-18 09:11:09.647849381 +0200
|
|
@@ -47,7 +47,7 @@
|
|
static void retire_buffer(MetadataOnStackBuffer* buffer);
|
|
|
|
public:
|
|
- MetadataOnStackMark(bool visit_code_cache);
|
|
+ MetadataOnStackMark(bool has_redefined_a_class);
|
|
~MetadataOnStackMark();
|
|
|
|
static void record(Metadata* m, Thread* thread);
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/code/nmethod.cpp 2018-09-18 09:10:03.879505910 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/code/nmethod.cpp 2018-09-18 09:11:09.651849402 +0200
|
|
@@ -2193,7 +2193,7 @@
|
|
"metadata must be found in exactly one place");
|
|
if (r->metadata_is_immediate() && r->metadata_value() != NULL) {
|
|
Metadata* md = r->metadata_value();
|
|
- f(md);
|
|
+ if (md != _method) f(md);
|
|
}
|
|
} else if (iter.type() == relocInfo::virtual_call_type) {
|
|
// Check compiledIC holders associated with this nmethod
|
|
@@ -2219,7 +2219,7 @@
|
|
f(md);
|
|
}
|
|
|
|
- // Visit metadata not embedded in the other places.
|
|
+ // Call function Method*, not embedded in these other places.
|
|
if (_method != NULL) f(_method);
|
|
}
|
|
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/instanceKlass.cpp 2018-09-18 09:10:03.891505972 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/instanceKlass.cpp 2018-09-18 09:11:09.651849402 +0200
|
|
@@ -2582,16 +2582,6 @@
|
|
assert(breakpoints() == 0x0, "should have cleared breakpoints");
|
|
}
|
|
|
|
- // deallocate information about previous versions
|
|
- if (_previous_versions != NULL) {
|
|
- for (int i = _previous_versions->length() - 1; i >= 0; i--) {
|
|
- PreviousVersionNode * pv_node = _previous_versions->at(i);
|
|
- delete pv_node;
|
|
- }
|
|
- delete _previous_versions;
|
|
- _previous_versions = NULL;
|
|
- }
|
|
-
|
|
// deallocate the cached class file
|
|
if (_cached_class_file != NULL) {
|
|
os::free(_cached_class_file, mtClass);
|
|
@@ -3204,16 +3194,17 @@
|
|
st->print(BULLET"field type annotations: "); fields_type_annotations()->print_value_on(st); st->cr();
|
|
{
|
|
bool have_pv = false;
|
|
- PreviousVersionWalker pvw(Thread::current(), (InstanceKlass*)this);
|
|
- for (PreviousVersionNode * pv_node = pvw.next_previous_version();
|
|
- pv_node != NULL; pv_node = pvw.next_previous_version()) {
|
|
+ // previous versions are linked together through the InstanceKlass
|
|
+ for (InstanceKlass* pv_node = _previous_versions;
|
|
+ pv_node != NULL;
|
|
+ pv_node = pv_node->previous_versions()) {
|
|
if (!have_pv)
|
|
st->print(BULLET"previous version: ");
|
|
have_pv = true;
|
|
- pv_node->prev_constant_pool()->print_value_on(st);
|
|
+ pv_node->constants()->print_value_on(st);
|
|
}
|
|
if (have_pv) st->cr();
|
|
- } // pvw is cleaned up
|
|
+ }
|
|
|
|
if (generic_signature() != NULL) {
|
|
st->print(BULLET"generic signature: ");
|
|
@@ -3627,92 +3618,93 @@
|
|
// RedefineClasses() support for previous versions:
|
|
|
|
// Purge previous versions
|
|
-static void purge_previous_versions_internal(InstanceKlass* ik, int emcp_method_count) {
|
|
+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.
|
|
|
|
int deleted_count = 0; // leave debugging breadcrumbs
|
|
int live_count = 0;
|
|
- ClassLoaderData* loader_data = ik->class_loader_data() == NULL ?
|
|
- ClassLoaderData::the_null_class_loader_data() :
|
|
- ik->class_loader_data();
|
|
+ ClassLoaderData* loader_data = ik->class_loader_data();
|
|
+ assert(loader_data != NULL, "should never be null");
|
|
|
|
// RC_TRACE macro has an embedded ResourceMark
|
|
- RC_TRACE(0x00000200, ("purge: %s: previous version length=%d",
|
|
- ik->external_name(), ik->previous_versions()->length()));
|
|
+ RC_TRACE(0x00000200, ("purge: %s: previous versions", ik->external_name()));
|
|
+
|
|
+ // previous versions are linked together through the InstanceKlass
|
|
+ InstanceKlass* pv_node = ik->previous_versions();
|
|
+ InstanceKlass* last = ik;
|
|
+ int version = 0;
|
|
+
|
|
+ // check the previous versions list
|
|
+ for (; pv_node != NULL; ) {
|
|
+
|
|
+ ConstantPool* pvcp = pv_node->constants();
|
|
+ assert(pvcp != NULL, "cp ref was unexpectedly cleared");
|
|
|
|
- for (int i = ik->previous_versions()->length() - 1; i >= 0; i--) {
|
|
- // check the previous versions array
|
|
- PreviousVersionNode * pv_node = ik->previous_versions()->at(i);
|
|
- ConstantPool* cp_ref = pv_node->prev_constant_pool();
|
|
- assert(cp_ref != NULL, "cp ref was unexpectedly cleared");
|
|
|
|
- ConstantPool* pvcp = cp_ref;
|
|
if (!pvcp->on_stack()) {
|
|
// If the constant pool isn't on stack, none of the methods
|
|
- // are executing. Delete all the methods, the constant pool and
|
|
- // and this previous version node.
|
|
- GrowableArray<Method*>* method_refs = pv_node->prev_EMCP_methods();
|
|
- if (method_refs != NULL) {
|
|
- for (int j = method_refs->length() - 1; j >= 0; j--) {
|
|
- Method* method = method_refs->at(j);
|
|
- assert(method != NULL, "method ref was unexpectedly cleared");
|
|
- method_refs->remove_at(j);
|
|
- // method will be freed with associated class.
|
|
- }
|
|
- }
|
|
- // Remove the constant pool
|
|
- delete pv_node;
|
|
- // Since we are traversing the array backwards, we don't have to
|
|
- // do anything special with the index.
|
|
- ik->previous_versions()->remove_at(i);
|
|
+ // 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.
|
|
+ pv_node = pv_node->previous_versions();
|
|
+ last->link_previous_versions(pv_node);
|
|
deleted_count++;
|
|
+ version++;
|
|
continue;
|
|
} else {
|
|
- RC_TRACE(0x00000200, ("purge: previous version @%d is alive", i));
|
|
+ RC_TRACE(0x00000200, ("purge: previous version " INTPTR_FORMAT " is alive",
|
|
+ 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++;
|
|
}
|
|
|
|
- // At least one method is live in this previous version, clean out
|
|
- // the others or mark them as obsolete.
|
|
- GrowableArray<Method*>* method_refs = pv_node->prev_EMCP_methods();
|
|
+ // At least one method is live in this previous version so clean its MethodData.
|
|
+ // 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) {
|
|
RC_TRACE(0x00000200, ("purge: previous methods length=%d",
|
|
method_refs->length()));
|
|
- for (int j = method_refs->length() - 1; j >= 0; j--) {
|
|
+ for (int j = 0; j < method_refs->length(); j++) {
|
|
Method* method = method_refs->at(j);
|
|
- assert(method != NULL, "method ref was unexpectedly cleared");
|
|
|
|
- // Remove the emcp method if it's not executing
|
|
- // If it's been made obsolete by a redefinition of a non-emcp
|
|
- // method, mark it as obsolete but leave it to clean up later.
|
|
if (!method->on_stack()) {
|
|
- method_refs->remove_at(j);
|
|
- } else if (emcp_method_count == 0) {
|
|
- method->set_is_obsolete();
|
|
+ // 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");
|
|
// RC_TRACE macro has an embedded ResourceMark
|
|
RC_TRACE(0x00000200,
|
|
("purge: %s(%s): prev method @%d in version @%d is alive",
|
|
method->name()->as_C_string(),
|
|
- method->signature()->as_C_string(), j, i));
|
|
+ method->signature()->as_C_string(), j, version));
|
|
if (method->method_data() != NULL) {
|
|
- // Clean out any weak method links
|
|
+ // Clean out any weak method links for running methods
|
|
+ // (also should include not EMCP methods)
|
|
method->method_data()->clean_weak_method_links();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
+ // next previous version
|
|
+ last = pv_node;
|
|
+ pv_node = pv_node->previous_versions();
|
|
+ version++;
|
|
}
|
|
- assert(ik->previous_versions()->length() == live_count, "sanity check");
|
|
RC_TRACE(0x00000200,
|
|
("purge: previous version stats: live=%d, deleted=%d", live_count,
|
|
deleted_count));
|
|
}
|
|
|
|
+ // Clean MethodData of this class's methods so they don't refer to
|
|
+ // old methods that are no longer running.
|
|
Array<Method*>* methods = ik->methods();
|
|
int num_methods = methods->length();
|
|
for (int index2 = 0; index2 < num_methods; ++index2) {
|
|
@@ -3722,122 +3714,30 @@
|
|
}
|
|
}
|
|
|
|
-// External interface for use during class unloading.
|
|
-void InstanceKlass::purge_previous_versions(InstanceKlass* ik) {
|
|
- // Call with >0 emcp methods since they are not currently being redefined.
|
|
- purge_previous_versions_internal(ik, 1);
|
|
-}
|
|
-
|
|
-
|
|
-// Potentially add an information node that contains pointers to the
|
|
-// interesting parts of the previous version of the_class.
|
|
-// This is also where we clean out any unused references.
|
|
-// Note that while we delete nodes from the _previous_versions
|
|
-// array, we never delete the array itself until the klass is
|
|
-// unloaded. The has_been_redefined() query depends on that fact.
|
|
-//
|
|
-void InstanceKlass::add_previous_version(instanceKlassHandle ikh,
|
|
- BitMap* emcp_methods, int emcp_method_count) {
|
|
- assert(Thread::current()->is_VM_thread(),
|
|
- "only VMThread can add previous versions");
|
|
-
|
|
- if (_previous_versions == NULL) {
|
|
- // This is the first previous version so make some space.
|
|
- // Start with 2 elements under the assumption that the class
|
|
- // won't be redefined much.
|
|
- _previous_versions = new (ResourceObj::C_HEAP, mtClass)
|
|
- GrowableArray<PreviousVersionNode *>(2, true);
|
|
- }
|
|
-
|
|
- ConstantPool* cp_ref = ikh->constants();
|
|
-
|
|
- // RC_TRACE macro has an embedded ResourceMark
|
|
- RC_TRACE(0x00000400, ("adding previous version ref for %s @%d, EMCP_cnt=%d "
|
|
- "on_stack=%d",
|
|
- ikh->external_name(), _previous_versions->length(), emcp_method_count,
|
|
- cp_ref->on_stack()));
|
|
-
|
|
- // If the constant pool for this previous version of the class
|
|
- // is not marked as being on the stack, then none of the methods
|
|
- // in this previous version of the class are on the stack so
|
|
- // we don't need to create a new PreviousVersionNode. However,
|
|
- // we still need to examine older previous versions below.
|
|
- Array<Method*>* old_methods = ikh->methods();
|
|
-
|
|
- if (cp_ref->on_stack()) {
|
|
- PreviousVersionNode * pv_node = NULL;
|
|
- if (emcp_method_count == 0) {
|
|
- // non-shared ConstantPool gets a reference
|
|
- pv_node = new PreviousVersionNode(cp_ref, NULL);
|
|
- RC_TRACE(0x00000400,
|
|
- ("add: all methods are obsolete; flushing any EMCP refs"));
|
|
- } else {
|
|
- int local_count = 0;
|
|
- GrowableArray<Method*>* method_refs = new (ResourceObj::C_HEAP, mtClass)
|
|
- GrowableArray<Method*>(emcp_method_count, true);
|
|
- for (int i = 0; i < old_methods->length(); i++) {
|
|
- if (emcp_methods->at(i)) {
|
|
- // this old method is EMCP. Save it only if it's on the stack
|
|
- Method* old_method = old_methods->at(i);
|
|
- if (old_method->on_stack()) {
|
|
- method_refs->append(old_method);
|
|
- }
|
|
- if (++local_count >= emcp_method_count) {
|
|
- // no more EMCP methods so bail out now
|
|
- break;
|
|
- }
|
|
- }
|
|
- }
|
|
- // non-shared ConstantPool gets a reference
|
|
- pv_node = new PreviousVersionNode(cp_ref, method_refs);
|
|
- }
|
|
- // append new previous version.
|
|
- _previous_versions->append(pv_node);
|
|
- }
|
|
-
|
|
- // Since the caller is the VMThread and we are at a safepoint, this
|
|
- // is a good time to clear out unused references.
|
|
-
|
|
- RC_TRACE(0x00000400, ("add: previous version length=%d",
|
|
- _previous_versions->length()));
|
|
-
|
|
- // Purge previous versions not executing on the stack
|
|
- purge_previous_versions_internal(this, emcp_method_count);
|
|
-
|
|
+void InstanceKlass::mark_newly_obsolete_methods(Array<Method*>* old_methods,
|
|
+ int emcp_method_count) {
|
|
int obsolete_method_count = old_methods->length() - emcp_method_count;
|
|
|
|
if (emcp_method_count != 0 && obsolete_method_count != 0 &&
|
|
- _previous_versions->length() > 0) {
|
|
+ _previous_versions != NULL) {
|
|
// We have a mix of obsolete and EMCP methods so we have to
|
|
// clear out any matching EMCP method entries the hard way.
|
|
int local_count = 0;
|
|
for (int i = 0; i < old_methods->length(); i++) {
|
|
- if (!emcp_methods->at(i)) {
|
|
- // only obsolete methods are interesting
|
|
Method* old_method = old_methods->at(i);
|
|
+ if (old_method->is_obsolete()) {
|
|
+ // only obsolete methods are interesting
|
|
Symbol* m_name = old_method->name();
|
|
Symbol* m_signature = old_method->signature();
|
|
|
|
- // we might not have added the last entry
|
|
- for (int j = _previous_versions->length() - 1; j >= 0; j--) {
|
|
- // check the previous versions array for non executing obsolete methods
|
|
- PreviousVersionNode * pv_node = _previous_versions->at(j);
|
|
-
|
|
- GrowableArray<Method*>* method_refs = pv_node->prev_EMCP_methods();
|
|
- if (method_refs == NULL) {
|
|
- // We have run into a PreviousVersion generation where
|
|
- // all methods were made obsolete during that generation's
|
|
- // RedefineClasses() operation. At the time of that
|
|
- // operation, all EMCP methods were flushed so we don't
|
|
- // have to go back any further.
|
|
- //
|
|
- // A NULL method_refs is different than an empty method_refs.
|
|
- // We cannot infer any optimizations about older generations
|
|
- // from an empty method_refs for the current generation.
|
|
- break;
|
|
- }
|
|
+ // previous versions are linked together through the InstanceKlass
|
|
+ int j = 0;
|
|
+ for (InstanceKlass* prev_version = _previous_versions;
|
|
+ prev_version != NULL;
|
|
+ prev_version = prev_version->previous_versions(), j++) {
|
|
|
|
- for (int k = method_refs->length() - 1; k >= 0; k--) {
|
|
+ Array<Method*>* method_refs = prev_version->methods();
|
|
+ for (int k = 0; k < method_refs->length(); k++) {
|
|
Method* method = method_refs->at(k);
|
|
|
|
if (!method->is_obsolete() &&
|
|
@@ -3845,14 +3745,11 @@
|
|
method->signature() == m_signature) {
|
|
// The current RedefineClasses() call has made all EMCP
|
|
// versions of this method obsolete so mark it as obsolete
|
|
- // and remove the reference.
|
|
RC_TRACE(0x00000400,
|
|
("add: %s(%s): flush obsolete method @%d in version @%d",
|
|
m_name->as_C_string(), m_signature->as_C_string(), k, j));
|
|
|
|
method->set_is_obsolete();
|
|
- // Leave obsolete methods on the previous version list to
|
|
- // clean up later.
|
|
break;
|
|
}
|
|
}
|
|
@@ -3860,9 +3757,9 @@
|
|
// The previous loop may not find a matching EMCP method, but
|
|
// that doesn't mean that we can optimize and not go any
|
|
// further back in the PreviousVersion generations. The EMCP
|
|
- // method for this generation could have already been deleted,
|
|
+ // method for this generation could have already been made obsolete,
|
|
// but there still may be an older EMCP method that has not
|
|
- // been deleted.
|
|
+ // been made obsolete.
|
|
}
|
|
|
|
if (++local_count >= obsolete_method_count) {
|
|
@@ -3872,30 +3769,67 @@
|
|
}
|
|
}
|
|
}
|
|
-} // end add_previous_version()
|
|
+}
|
|
|
|
+// Save the scratch_class as the previous version if any of the methods are running.
|
|
+// The previous_versions are used to set breakpoints in EMCP methods and they are
|
|
+// also used to clean MethodData links to redefined methods that are no longer running.
|
|
+void InstanceKlass::add_previous_version(instanceKlassHandle scratch_class,
|
|
+ int emcp_method_count) {
|
|
+ assert(Thread::current()->is_VM_thread(),
|
|
+ "only VMThread can add previous versions");
|
|
|
|
-// Determine if InstanceKlass has a previous version.
|
|
-bool InstanceKlass::has_previous_version() const {
|
|
- return (_previous_versions != NULL && _previous_versions->length() > 0);
|
|
-} // end has_previous_version()
|
|
+ // RC_TRACE macro has an embedded ResourceMark
|
|
+ RC_TRACE(0x00000400, ("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);
|
|
|
|
-InstanceKlass* InstanceKlass::get_klass_version(int version) {
|
|
- if (constants()->version() == version) {
|
|
- return this;
|
|
+ // Mark newly obsolete methods in remaining previous versions. An EMCP method from
|
|
+ // a previous redefinition may be made obsolete by this redefinition.
|
|
+ Array<Method*>* old_methods = scratch_class->methods();
|
|
+ mark_newly_obsolete_methods(old_methods, emcp_method_count);
|
|
+
|
|
+ // If the constant pool for this previous version of the class
|
|
+ // is not marked as being on the stack, then none of the methods
|
|
+ // in this previous version of the class are on the stack so
|
|
+ // we don't need to add this as a previous version.
|
|
+ ConstantPool* cp_ref = scratch_class->constants();
|
|
+ if (!cp_ref->on_stack()) {
|
|
+ RC_TRACE(0x00000400, ("add: scratch class not added; no methods are running"));
|
|
+ return;
|
|
}
|
|
- PreviousVersionWalker pvw(Thread::current(), (InstanceKlass*)this);
|
|
- for (PreviousVersionNode * pv_node = pvw.next_previous_version();
|
|
- pv_node != NULL; pv_node = pvw.next_previous_version()) {
|
|
- ConstantPool* prev_cp = pv_node->prev_constant_pool();
|
|
- if (prev_cp->version() == version) {
|
|
- return prev_cp->pool_holder();
|
|
+
|
|
+ if (emcp_method_count != 0) {
|
|
+ // At least one method is still running, check for EMCP methods
|
|
+ for (int i = 0; i < old_methods->length(); i++) {
|
|
+ Method* old_method = old_methods->at(i);
|
|
+ if (!old_method->is_obsolete() && old_method->on_stack()) {
|
|
+ // if EMCP method (not obsolete) is on the stack, mark as EMCP so that
|
|
+ // we can add breakpoints for it.
|
|
+
|
|
+ // We set the method->on_stack bit during safepoints for class redefinition and
|
|
+ // class unloading and use this bit to set the is_running_emcp bit.
|
|
+ // After the safepoint, the on_stack bit is cleared and the running emcp
|
|
+ // method may exit. If so, we would set a breakpoint in a method that
|
|
+ // is never reached, but this won't be noticeable to the programmer.
|
|
+ old_method->set_running_emcp(true);
|
|
+ RC_TRACE(0x00000400, ("add: EMCP method %s is on_stack " INTPTR_FORMAT,
|
|
+ old_method->name_and_sig_as_C_string(), old_method));
|
|
+ } else if (!old_method->is_obsolete()) {
|
|
+ RC_TRACE(0x00000400, ("add: EMCP method %s is NOT on_stack " INTPTR_FORMAT,
|
|
+ old_method->name_and_sig_as_C_string(), old_method));
|
|
}
|
|
}
|
|
- return NULL; // None found
|
|
}
|
|
|
|
+ // Add previous version if any methods are still running.
|
|
+ RC_TRACE(0x00000400, ("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());
|
|
+} // end add_previous_version()
|
|
|
|
Method* InstanceKlass::method_with_idnum(int idnum) {
|
|
Method* m = NULL;
|
|
@@ -3953,61 +3887,3 @@
|
|
unsigned char * InstanceKlass::get_cached_class_file_bytes() {
|
|
return VM_RedefineClasses::get_cached_class_file_bytes(_cached_class_file);
|
|
}
|
|
-
|
|
-
|
|
-// Construct a PreviousVersionNode entry for the array hung off
|
|
-// the InstanceKlass.
|
|
-PreviousVersionNode::PreviousVersionNode(ConstantPool* prev_constant_pool,
|
|
- GrowableArray<Method*>* prev_EMCP_methods) {
|
|
-
|
|
- _prev_constant_pool = prev_constant_pool;
|
|
- _prev_EMCP_methods = prev_EMCP_methods;
|
|
-}
|
|
-
|
|
-
|
|
-// Destroy a PreviousVersionNode
|
|
-PreviousVersionNode::~PreviousVersionNode() {
|
|
- if (_prev_constant_pool != NULL) {
|
|
- _prev_constant_pool = NULL;
|
|
- }
|
|
-
|
|
- if (_prev_EMCP_methods != NULL) {
|
|
- delete _prev_EMCP_methods;
|
|
- }
|
|
-}
|
|
-
|
|
-// Construct a helper for walking the previous versions array
|
|
-PreviousVersionWalker::PreviousVersionWalker(Thread* thread, InstanceKlass *ik) {
|
|
- _thread = thread;
|
|
- _previous_versions = ik->previous_versions();
|
|
- _current_index = 0;
|
|
- _current_p = NULL;
|
|
- _current_constant_pool_handle = constantPoolHandle(thread, ik->constants());
|
|
-}
|
|
-
|
|
-
|
|
-// Return the interesting information for the next previous version
|
|
-// of the klass. Returns NULL if there are no more previous versions.
|
|
-PreviousVersionNode* PreviousVersionWalker::next_previous_version() {
|
|
- if (_previous_versions == NULL) {
|
|
- // no previous versions so nothing to return
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- _current_p = NULL; // reset to NULL
|
|
- _current_constant_pool_handle = NULL;
|
|
-
|
|
- int length = _previous_versions->length();
|
|
-
|
|
- while (_current_index < length) {
|
|
- PreviousVersionNode * pv_node = _previous_versions->at(_current_index++);
|
|
-
|
|
- // Save a handle to the constant pool for this previous version,
|
|
- // which keeps all the methods from being deallocated.
|
|
- _current_constant_pool_handle = constantPoolHandle(_thread, pv_node->prev_constant_pool());
|
|
- _current_p = pv_node;
|
|
- return pv_node;
|
|
- }
|
|
-
|
|
- return NULL;
|
|
-} // end next_previous_version()
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/instanceKlass.hpp 2018-09-18 09:10:03.891505972 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/instanceKlass.hpp 2018-09-18 09:11:09.651849402 +0200
|
|
@@ -88,7 +88,6 @@
|
|
class fieldDescriptor;
|
|
class DepChange;
|
|
class nmethodBucket;
|
|
-class PreviousVersionNode;
|
|
class JvmtiCachedClassFieldMap;
|
|
class MemberNameTable;
|
|
|
|
@@ -235,7 +234,8 @@
|
|
_misc_is_anonymous = 1 << 3, // has embedded _host_klass field
|
|
_misc_is_contended = 1 << 4, // marked with contended annotation
|
|
_misc_has_default_methods = 1 << 5, // class/superclass/implemented interfaces has default methods
|
|
- _misc_declares_default_methods = 1 << 6 // directly declares default methods (any access)
|
|
+ _misc_declares_default_methods = 1 << 6, // directly declares default methods (any access)
|
|
+ _misc_has_been_redefined = 1 << 7 // class has been redefined
|
|
};
|
|
u2 _misc_flags;
|
|
u2 _minor_version; // minor version number of class file
|
|
@@ -250,9 +250,8 @@
|
|
nmethodBucket* _dependencies; // list of dependent nmethods
|
|
nmethod* _osr_nmethods_head; // Head of list of on-stack replacement nmethods for this class
|
|
BreakpointInfo* _breakpoints; // bpt lists, managed by Method*
|
|
- // Array of interesting part(s) of the previous version(s) of this
|
|
- // InstanceKlass. See PreviousVersionWalker below.
|
|
- GrowableArray<PreviousVersionNode *>* _previous_versions;
|
|
+ // Linked instanceKlasses of previous versions
|
|
+ InstanceKlass* _previous_versions;
|
|
// JVMTI fields can be moved to their own structure - see 6315920
|
|
// JVMTI: cached class file, before retransformable agent modified it in CFLH
|
|
JvmtiCachedClassFileData* _cached_class_file;
|
|
@@ -669,21 +668,31 @@
|
|
}
|
|
|
|
// RedefineClasses() support for previous versions:
|
|
- void add_previous_version(instanceKlassHandle ikh, BitMap *emcp_methods,
|
|
- int emcp_method_count);
|
|
- // If the _previous_versions array is non-NULL, then this klass
|
|
- // has been redefined at least once even if we aren't currently
|
|
- // tracking a previous version.
|
|
- bool has_been_redefined() const { return _previous_versions != NULL; }
|
|
- bool has_previous_version() const;
|
|
+ void add_previous_version(instanceKlassHandle ikh, int emcp_method_count);
|
|
+
|
|
+ InstanceKlass* previous_versions() const { return _previous_versions; }
|
|
+
|
|
+ bool has_been_redefined() const {
|
|
+ return (_misc_flags & _misc_has_been_redefined) != 0;
|
|
+ }
|
|
+ void set_has_been_redefined() {
|
|
+ _misc_flags |= _misc_has_been_redefined;
|
|
+ }
|
|
+
|
|
void init_previous_versions() {
|
|
_previous_versions = NULL;
|
|
}
|
|
- GrowableArray<PreviousVersionNode *>* previous_versions() const {
|
|
- return _previous_versions;
|
|
+
|
|
+
|
|
+ InstanceKlass* get_klass_version(int version) {
|
|
+ for (InstanceKlass* ik = this; ik != NULL; ik = ik->previous_versions()) {
|
|
+ if (ik->constants()->version() == version) {
|
|
+ return ik;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
}
|
|
|
|
- InstanceKlass* get_klass_version(int version);
|
|
static void purge_previous_versions(InstanceKlass* ik);
|
|
|
|
// JVMTI: Support for caching a class file before it is modified by an agent that can do retransformation
|
|
@@ -1124,6 +1133,10 @@
|
|
|
|
// Free CHeap allocated fields.
|
|
void release_C_heap_structures();
|
|
+
|
|
+ // RedefineClasses support
|
|
+ void link_previous_versions(InstanceKlass* pv) { _previous_versions = pv; }
|
|
+ void mark_newly_obsolete_methods(Array<Method*>* old_methods, int emcp_method_count);
|
|
public:
|
|
// CDS support - remove and restore oops from metadata. Oops are not shared.
|
|
virtual void remove_unshareable_info();
|
|
@@ -1222,62 +1235,6 @@
|
|
};
|
|
|
|
|
|
-// If breakpoints are more numerous than just JVMTI breakpoints,
|
|
-// consider compressing this data structure.
|
|
-// It is currently a simple linked list defined in method.hpp.
|
|
-
|
|
-class BreakpointInfo;
|
|
-
|
|
-
|
|
-// A collection point for interesting information about the previous
|
|
-// version(s) of an InstanceKlass. A GrowableArray of PreviousVersionNodes
|
|
-// is attached to the InstanceKlass as needed. See PreviousVersionWalker below.
|
|
-class PreviousVersionNode : public CHeapObj<mtClass> {
|
|
- private:
|
|
- ConstantPool* _prev_constant_pool;
|
|
-
|
|
- // If the previous version of the InstanceKlass doesn't have any
|
|
- // EMCP methods, then _prev_EMCP_methods will be NULL. If all the
|
|
- // EMCP methods have been collected, then _prev_EMCP_methods can
|
|
- // have a length of zero.
|
|
- GrowableArray<Method*>* _prev_EMCP_methods;
|
|
-
|
|
-public:
|
|
- PreviousVersionNode(ConstantPool* prev_constant_pool,
|
|
- GrowableArray<Method*>* prev_EMCP_methods);
|
|
- ~PreviousVersionNode();
|
|
- ConstantPool* prev_constant_pool() const {
|
|
- return _prev_constant_pool;
|
|
- }
|
|
- GrowableArray<Method*>* prev_EMCP_methods() const {
|
|
- return _prev_EMCP_methods;
|
|
- }
|
|
-};
|
|
-
|
|
-
|
|
-// Helper object for walking previous versions.
|
|
-class PreviousVersionWalker : public StackObj {
|
|
- private:
|
|
- Thread* _thread;
|
|
- GrowableArray<PreviousVersionNode *>* _previous_versions;
|
|
- int _current_index;
|
|
-
|
|
- // A pointer to the current node object so we can handle the deletes.
|
|
- PreviousVersionNode* _current_p;
|
|
-
|
|
- // The constant pool handle keeps all the methods in this class from being
|
|
- // deallocated from the metaspace during class unloading.
|
|
- constantPoolHandle _current_constant_pool_handle;
|
|
-
|
|
- public:
|
|
- PreviousVersionWalker(Thread* thread, InstanceKlass *ik);
|
|
-
|
|
- // Return the interesting information for the next previous version
|
|
- // of the klass. Returns NULL if there are no more previous versions.
|
|
- PreviousVersionNode* next_previous_version();
|
|
-};
|
|
-
|
|
-
|
|
//
|
|
// nmethodBucket is used to record dependent nmethods for
|
|
// deoptimization. nmethod dependencies are actually <klass, method>
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/klass.cpp 2018-09-18 09:10:03.891505972 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/klass.cpp 2018-09-18 09:11:09.651849402 +0200
|
|
@@ -468,6 +468,12 @@
|
|
if (clean_alive_klasses && current->oop_is_instance()) {
|
|
InstanceKlass* ik = InstanceKlass::cast(current);
|
|
ik->clean_weak_instanceklass_links(is_alive);
|
|
+
|
|
+ // JVMTI RedefineClasses creates previous versions that are not in
|
|
+ // the class hierarchy, so process them here.
|
|
+ while ((ik = ik->previous_versions()) != NULL) {
|
|
+ ik->clean_weak_instanceklass_links(is_alive);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/method.cpp 2018-09-18 09:10:03.891505972 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/method.cpp 2018-09-18 09:11:09.655849423 +0200
|
|
@@ -91,6 +91,7 @@
|
|
set_hidden(false);
|
|
set_dont_inline(false);
|
|
set_has_injected_profile(false);
|
|
+ set_running_emcp(false);
|
|
set_method_data(NULL);
|
|
clear_method_counters();
|
|
set_vtable_index(Method::garbage_vtable_index);
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/method.hpp 2018-09-18 09:10:03.891505972 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/oops/method.hpp 2018-09-18 09:11:09.655849423 +0200
|
|
@@ -110,6 +110,7 @@
|
|
_caller_sensitive : 1,
|
|
_force_inline : 1,
|
|
_hidden : 1,
|
|
+ _running_emcp : 1,
|
|
_dont_inline : 1,
|
|
_has_injected_profile : 1,
|
|
: 2;
|
|
@@ -717,6 +718,21 @@
|
|
void set_is_obsolete() { _access_flags.set_is_obsolete(); }
|
|
bool is_deleted() const { return access_flags().is_deleted(); }
|
|
void set_is_deleted() { _access_flags.set_is_deleted(); }
|
|
+
|
|
+ bool is_running_emcp() const {
|
|
+ // EMCP methods are old but not obsolete or deleted. Equivalent
|
|
+ // Modulo Constant Pool means the method is equivalent except
|
|
+ // the constant pool and instructions that access the constant
|
|
+ // pool might be different.
|
|
+ // If a breakpoint is set in a redefined method, its EMCP methods that are
|
|
+ // still running must have a breakpoint also.
|
|
+ return _running_emcp;
|
|
+ }
|
|
+
|
|
+ void set_running_emcp(bool x) {
|
|
+ _running_emcp = x;
|
|
+ }
|
|
+
|
|
bool on_stack() const { return access_flags().on_stack(); }
|
|
void set_on_stack(const bool value);
|
|
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/prims/jvmtiImpl.cpp 2018-09-18 09:10:03.895505993 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/prims/jvmtiImpl.cpp 2018-09-18 09:11:09.655849423 +0200
|
|
@@ -282,39 +282,22 @@
|
|
void JvmtiBreakpoint::each_method_version_do(method_action meth_act) {
|
|
((Method*)_method->*meth_act)(_bci);
|
|
|
|
- // add/remove breakpoint to/from versions of the method that
|
|
- // are EMCP. Directly or transitively obsolete methods are
|
|
- // not saved in the PreviousVersionNodes.
|
|
+ // add/remove breakpoint to/from versions of the method that are EMCP.
|
|
Thread *thread = Thread::current();
|
|
instanceKlassHandle ikh = instanceKlassHandle(thread, _method->method_holder());
|
|
Symbol* m_name = _method->name();
|
|
Symbol* m_signature = _method->signature();
|
|
|
|
// search previous versions if they exist
|
|
- PreviousVersionWalker pvw(thread, (InstanceKlass *)ikh());
|
|
- for (PreviousVersionNode * pv_node = pvw.next_previous_version();
|
|
- pv_node != NULL; pv_node = pvw.next_previous_version()) {
|
|
- GrowableArray<Method*>* methods = pv_node->prev_EMCP_methods();
|
|
-
|
|
- if (methods == NULL) {
|
|
- // We have run into a PreviousVersion generation where
|
|
- // all methods were made obsolete during that generation's
|
|
- // RedefineClasses() operation. At the time of that
|
|
- // operation, all EMCP methods were flushed so we don't
|
|
- // have to go back any further.
|
|
- //
|
|
- // A NULL methods array is different than an empty methods
|
|
- // array. We cannot infer any optimizations about older
|
|
- // generations from an empty methods array for the current
|
|
- // generation.
|
|
- break;
|
|
- }
|
|
+ for (InstanceKlass* pv_node = ikh->previous_versions();
|
|
+ pv_node != NULL;
|
|
+ pv_node = pv_node->previous_versions()) {
|
|
+ Array<Method*>* methods = pv_node->methods();
|
|
|
|
for (int i = methods->length() - 1; i >= 0; i--) {
|
|
Method* method = methods->at(i);
|
|
- // obsolete methods that are running are not deleted from
|
|
- // previous version array, but they are skipped here.
|
|
- if (!method->is_obsolete() &&
|
|
+ // Only set breakpoints in running EMCP methods.
|
|
+ if (method->is_running_emcp() &&
|
|
method->name() == m_name &&
|
|
method->signature() == m_signature) {
|
|
RC_TRACE(0x00000800, ("%sing breakpoint in %s(%s)",
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp 2018-09-18 09:10:03.895505993 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp 2018-09-18 09:11:09.655849423 +0200
|
|
@@ -3435,13 +3435,12 @@
|
|
}
|
|
|
|
// the previous versions' constant pool caches may need adjustment
|
|
- PreviousVersionWalker pvw(_thread, ik);
|
|
- for (PreviousVersionNode * pv_node = pvw.next_previous_version();
|
|
- pv_node != NULL; pv_node = pvw.next_previous_version()) {
|
|
- other_cp = pv_node->prev_constant_pool();
|
|
- cp_cache = other_cp->cache();
|
|
+ for (InstanceKlass* pv_node = ik->previous_versions();
|
|
+ pv_node != NULL;
|
|
+ pv_node = pv_node->previous_versions()) {
|
|
+ cp_cache = pv_node->constants()->cache();
|
|
if (cp_cache != NULL) {
|
|
- cp_cache->adjust_method_entries(other_cp->pool_holder(), &trace_name_printed);
|
|
+ cp_cache->adjust_method_entries(pv_node, &trace_name_printed);
|
|
}
|
|
}
|
|
}
|
|
@@ -3461,9 +3460,8 @@
|
|
}
|
|
}
|
|
|
|
-void VM_RedefineClasses::check_methods_and_mark_as_obsolete(
|
|
- BitMap *emcp_methods, int * emcp_method_count_p) {
|
|
- *emcp_method_count_p = 0;
|
|
+int VM_RedefineClasses::check_methods_and_mark_as_obsolete() {
|
|
+ int emcp_method_count = 0;
|
|
int obsolete_count = 0;
|
|
int old_index = 0;
|
|
for (int j = 0; j < _matching_methods_length; ++j, ++old_index) {
|
|
@@ -3537,9 +3535,9 @@
|
|
// that we get from effectively overwriting the old methods
|
|
// when the new methods are attached to the_class.
|
|
|
|
- // track which methods are EMCP for add_previous_version() call
|
|
- emcp_methods->set_bit(old_index);
|
|
- (*emcp_method_count_p)++;
|
|
+ // Count number of methods that are EMCP. The method will be marked
|
|
+ // old but not obsolete if it is EMCP.
|
|
+ emcp_method_count++;
|
|
|
|
// An EMCP method is _not_ obsolete. An obsolete method has a
|
|
// different jmethodID than the current method. An EMCP method
|
|
@@ -3589,10 +3587,11 @@
|
|
old_method->name()->as_C_string(),
|
|
old_method->signature()->as_C_string()));
|
|
}
|
|
- assert((*emcp_method_count_p + obsolete_count) == _old_methods->length(),
|
|
+ assert((emcp_method_count + obsolete_count) == _old_methods->length(),
|
|
"sanity check");
|
|
- RC_TRACE(0x00000100, ("EMCP_cnt=%d, obsolete_cnt=%d", *emcp_method_count_p,
|
|
+ RC_TRACE(0x00000100, ("EMCP_cnt=%d, obsolete_cnt=%d", emcp_method_count,
|
|
obsolete_count));
|
|
+ return emcp_method_count;
|
|
}
|
|
|
|
// This internal class transfers the native function registration from old methods
|
|
@@ -3973,11 +3972,8 @@
|
|
old_constants->set_pool_holder(scratch_class());
|
|
#endif
|
|
|
|
- // track which methods are EMCP for add_previous_version() call below
|
|
- BitMap emcp_methods(_old_methods->length());
|
|
- int emcp_method_count = 0;
|
|
- emcp_methods.clear(); // clears 0..(length() - 1)
|
|
- check_methods_and_mark_as_obsolete(&emcp_methods, &emcp_method_count);
|
|
+ // track number of methods that are EMCP for add_previous_version() call below
|
|
+ int emcp_method_count = check_methods_and_mark_as_obsolete();
|
|
transfer_old_native_function_registrations(the_class);
|
|
|
|
// The class file bytes from before any retransformable agents mucked
|
|
@@ -4064,9 +4060,10 @@
|
|
scratch_class->enclosing_method_method_index());
|
|
scratch_class->set_enclosing_method_indices(old_class_idx, old_method_idx);
|
|
|
|
+ the_class->set_has_been_redefined();
|
|
+
|
|
// keep track of previous versions of this class
|
|
- the_class->add_previous_version(scratch_class, &emcp_methods,
|
|
- emcp_method_count);
|
|
+ the_class->add_previous_version(scratch_class, emcp_method_count);
|
|
|
|
RC_TIMER_STOP(_timer_rsc_phase1);
|
|
RC_TIMER_START(_timer_rsc_phase2);
|
|
--- icedtea-3.8.0/openjdk/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp 2018-09-18 09:10:03.895505993 +0200
|
|
+++ icedtea-3.8.0/openjdk/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp 2018-09-18 09:11:09.655849423 +0200
|
|
@@ -403,14 +403,9 @@
|
|
// Change jmethodIDs to point to the new methods
|
|
void update_jmethod_ids();
|
|
|
|
- // In addition to marking methods as obsolete, this routine
|
|
- // records which methods are EMCP (Equivalent Module Constant
|
|
- // Pool) in the emcp_methods BitMap and returns the number of
|
|
- // EMCP methods via emcp_method_count_p. This information is
|
|
- // used when information about the previous version of the_class
|
|
- // is squirreled away.
|
|
- void check_methods_and_mark_as_obsolete(BitMap *emcp_methods,
|
|
- int * emcp_method_count_p);
|
|
+ // In addition to marking methods as old and/or obsolete, this routine
|
|
+ // counts the number of methods that are EMCP (Equivalent Module Constant Pool).
|
|
+ int check_methods_and_mark_as_obsolete();
|
|
void transfer_old_native_function_registrations(instanceKlassHandle the_class);
|
|
|
|
// Install the redefinition of a class
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ icedtea-3.8.0/openjdk/hotspot/test/runtime/RedefineTests/RedefineFinalizer.java Mon Jan 08 08:32:04 2018 -0800
|
|
@@ -0,0 +1,64 @@
|
|
+/*
|
|
+ * Copyright (c) 2014, 2018, 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
|
|
+ * under the terms of the GNU General Public License version 2 only, as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * This code is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
+ * version 2 for more details (a copy is included in the LICENSE file that
|
|
+ * accompanied this code).
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License version
|
|
+ * 2 along with this work; if not, write to the Free Software Foundation,
|
|
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
+ * or visit www.oracle.com if you need additional information or have any
|
|
+ * questions.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * @test
|
|
+ * @bug 6904403
|
|
+ * @summary Don't assert if we redefine finalize method
|
|
+ * @library /testlibrary
|
|
+ * @build RedefineClassHelper
|
|
+ * @run main RedefineClassHelper
|
|
+ * @run main/othervm -javaagent:redefineagent.jar RedefineFinalizer
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Regression test for hitting:
|
|
+ *
|
|
+ * assert(f == k->has_finalizer()) failed: inconsistent has_finalizer
|
|
+ *
|
|
+ * when redefining finalizer method
|
|
+ */
|
|
+public class RedefineFinalizer {
|
|
+
|
|
+ public static String newB =
|
|
+ "class RedefineFinalizer$B {" +
|
|
+ " protected void finalize() { " +
|
|
+ " System.out.println(\"Finalizer called\");" +
|
|
+ " }" +
|
|
+ "}";
|
|
+
|
|
+ public static void main(String[] args) throws Exception {
|
|
+ RedefineClassHelper.redefineClass(B.class, newB);
|
|
+
|
|
+ A a = new A();
|
|
+ }
|
|
+
|
|
+ static class A extends B {
|
|
+ }
|
|
+
|
|
+ static class B {
|
|
+ protected void finalize() {
|
|
+ // should be empty
|
|
+ }
|
|
+ }
|
|
+}
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ icedtea-3.8.0/openjdk/hotspot/test/runtime/RedefineTests/RedefineRunningMethods.java Mon Jan 08 08:32:04 2018 -0800
|
|
@@ -0,0 +1,143 @@
|
|
+/*
|
|
+ * Copyright (c) 2014, 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
|
|
+ * under the terms of the GNU General Public License version 2 only, as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * This code is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
+ * version 2 for more details (a copy is included in the LICENSE file that
|
|
+ * accompanied this code).
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License version
|
|
+ * 2 along with this work; if not, write to the Free Software Foundation,
|
|
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
+ * or visit www.oracle.com if you need additional information or have any
|
|
+ * questions.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * @test
|
|
+ * @bug 8055008
|
|
+ * @summary Redefine EMCP and non-EMCP methods that are running in an infinite loop
|
|
+ * @library /testlibrary
|
|
+ * @build RedefineClassHelper
|
|
+ * @run main RedefineClassHelper
|
|
+ * @run main/othervm -javaagent:redefineagent.jar RedefineRunningMethods
|
|
+ */
|
|
+public class RedefineRunningMethods {
|
|
+
|
|
+ public static String newB =
|
|
+ "class RedefineRunningMethods$B {" +
|
|
+ " static int count1 = 0;" +
|
|
+ " static int count2 = 0;" +
|
|
+ " public static volatile boolean stop = false;" +
|
|
+ " static void localSleep() { " +
|
|
+ " try{ " +
|
|
+ " Thread.currentThread().sleep(10);" +
|
|
+ " } catch(InterruptedException ie) { " +
|
|
+ " } " +
|
|
+ " } " +
|
|
+ " public static void infinite() { " +
|
|
+ " System.out.println(\"infinite called\");" +
|
|
+ " }" +
|
|
+ " public static void infinite_emcp() { " +
|
|
+ " while (!stop) { count2++; localSleep(); }" +
|
|
+ " }" +
|
|
+ "}";
|
|
+
|
|
+ public static String evenNewerB =
|
|
+ "class RedefineRunningMethods$B {" +
|
|
+ " static int count1 = 0;" +
|
|
+ " static int count2 = 0;" +
|
|
+ " public static volatile boolean stop = false;" +
|
|
+ " static void localSleep() { " +
|
|
+ " try{ " +
|
|
+ " Thread.currentThread().sleep(1);" +
|
|
+ " } catch(InterruptedException ie) { " +
|
|
+ " } " +
|
|
+ " } " +
|
|
+ " public static void infinite() { }" +
|
|
+ " public static void infinite_emcp() { " +
|
|
+ " System.out.println(\"infinite_emcp now obsolete called\");" +
|
|
+ " }" +
|
|
+ "}";
|
|
+
|
|
+ static class B {
|
|
+ static int count1 = 0;
|
|
+ static int count2 = 0;
|
|
+ public static volatile boolean stop = false;
|
|
+ static void localSleep() {
|
|
+ try{
|
|
+ Thread.currentThread().sleep(10);//sleep for 10 ms
|
|
+ } catch(InterruptedException ie) {
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void infinite() {
|
|
+ while (!stop) { count1++; localSleep(); }
|
|
+ }
|
|
+ public static void infinite_emcp() {
|
|
+ while (!stop) { count2++; localSleep(); }
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ public static void main(String[] args) throws Exception {
|
|
+
|
|
+ new Thread() {
|
|
+ public void run() {
|
|
+ B.infinite();
|
|
+ }
|
|
+ }.start();
|
|
+
|
|
+ new Thread() {
|
|
+ public void run() {
|
|
+ B.infinite_emcp();
|
|
+ }
|
|
+ }.start();
|
|
+
|
|
+ RedefineClassHelper.redefineClass(B.class, newB);
|
|
+
|
|
+ System.gc();
|
|
+
|
|
+ B.infinite();
|
|
+
|
|
+ // Start a thread with the second version of infinite_emcp running
|
|
+ new Thread() {
|
|
+ public void run() {
|
|
+ B.infinite_emcp();
|
|
+ }
|
|
+ }.start();
|
|
+
|
|
+ for (int i = 0; i < 20 ; i++) {
|
|
+ String s = new String("some garbage");
|
|
+ System.gc();
|
|
+ }
|
|
+
|
|
+ RedefineClassHelper.redefineClass(B.class, evenNewerB);
|
|
+ System.gc();
|
|
+
|
|
+ for (int i = 0; i < 20 ; i++) {
|
|
+ B.infinite();
|
|
+ String s = new String("some garbage");
|
|
+ System.gc();
|
|
+ }
|
|
+
|
|
+ B.infinite_emcp();
|
|
+
|
|
+ // purge should clean everything up.
|
|
+ B.stop = true;
|
|
+
|
|
+ for (int i = 0; i < 20 ; i++) {
|
|
+ B.infinite();
|
|
+ String s = new String("some garbage");
|
|
+ System.gc();
|
|
+ }
|
|
+ }
|
|
+}
|