diff --git a/src/share/vm/classfile/metadataOnStackMark.cpp b/src/share/vm/classfile/metadataOnStackMark.cpp index 4c9b4efb6cb5ba4b9b010635884ac7ec297fe727..ade3a863e4b77a46d6380a598ac32a3298821a38 100644 --- a/src/share/vm/classfile/metadataOnStackMark.cpp +++ b/src/share/vm/classfile/metadataOnStackMark.cpp @@ -41,13 +41,13 @@ NOT_PRODUCT(bool MetadataOnStackMark::_is_active = false;) // 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(); diff --git a/src/share/vm/classfile/metadataOnStackMark.hpp b/src/share/vm/classfile/metadataOnStackMark.hpp index 9233c2608dd506a9c8caa567af56e12a498e0ee9..e0d5c76dbfb762999ccd19a712cdaf6eac0f6b2e 100644 --- a/src/share/vm/classfile/metadataOnStackMark.hpp +++ b/src/share/vm/classfile/metadataOnStackMark.hpp @@ -47,7 +47,7 @@ class MetadataOnStackMark : public StackObj { 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); diff --git a/src/share/vm/code/nmethod.cpp b/src/share/vm/code/nmethod.cpp index b897b6a7ac056a554b9b36a9a839d7f7e2596662..2693d68d67c0eddd0a889654964a00201af46d72 100644 --- a/src/share/vm/code/nmethod.cpp +++ b/src/share/vm/code/nmethod.cpp @@ -2173,7 +2173,7 @@ void nmethod::metadata_do(void f(Metadata*)) { "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 @@ -2199,7 +2199,7 @@ void nmethod::metadata_do(void f(Metadata*)) { f(md); } - // Visit metadata not embedded in the other places. + // Call function Method*, not embedded in these other places. if (_method != NULL) f(_method); } diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp index 22b868232a5f67fad095bbe2f4f20a63e0f22a88..f8f531d24f08eb9824e22b1da3d3402b70fdb7ea 100644 --- a/src/share/vm/oops/instanceKlass.cpp +++ b/src/share/vm/oops/instanceKlass.cpp @@ -2582,16 +2582,6 @@ void InstanceKlass::release_C_heap_structures() { 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); @@ -3187,16 +3177,17 @@ void InstanceKlass::print_on(outputStream* st) const { 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: "); @@ -3610,92 +3601,93 @@ void InstanceKlass::set_init_state(ClassState state) { // 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_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_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_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* methods = ik->methods(); int num_methods = methods->length(); for (int index2 = 0; index2 < num_methods; ++index2) { @@ -3705,122 +3697,30 @@ static void purge_previous_versions_internal(InstanceKlass* ik, int emcp_method_ } } -// 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(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* 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_refs = new (ResourceObj::C_HEAP, mtClass) - GrowableArray(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* 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)) { + Method* old_method = old_methods->at(i); + if (old_method->is_obsolete()) { // only obsolete methods are interesting - Method* old_method = old_methods->at(i); 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_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_refs = prev_version->methods(); + for (int k = 0; k < method_refs->length(); k++) { Method* method = method_refs->at(k); if (!method->is_obsolete() && @@ -3828,14 +3728,11 @@ void InstanceKlass::add_previous_version(instanceKlassHandle ikh, 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; } } @@ -3843,9 +3740,9 @@ void InstanceKlass::add_previous_version(instanceKlassHandle ikh, // 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) { @@ -3855,30 +3752,67 @@ void InstanceKlass::add_previous_version(instanceKlassHandle ikh, } } } -} // 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"); + // 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)); -// 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() + // Clean out old previous versions + purge_previous_versions(this); + // Mark newly obsolete methods in remaining previous versions. An EMCP method from + // a previous redefinition may be made obsolete by this redefinition. + Array* old_methods = scratch_class->methods(); + mark_newly_obsolete_methods(old_methods, emcp_method_count); -InstanceKlass* InstanceKlass::get_klass_version(int version) { - if (constants()->version() == version) { - return this; + // 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; @@ -3936,61 +3870,3 @@ jint InstanceKlass::get_cached_class_file_len() { 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* 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() diff --git a/src/share/vm/oops/instanceKlass.hpp b/src/share/vm/oops/instanceKlass.hpp index e1fba31c1fa7c791f2d98240e403e4bbd668810f..a091af27475824124bfe6137eef2bded3409ed43 100644 --- a/src/share/vm/oops/instanceKlass.hpp +++ b/src/share/vm/oops/instanceKlass.hpp @@ -88,7 +88,6 @@ class BreakpointInfo; class fieldDescriptor; class DepChange; class nmethodBucket; -class PreviousVersionNode; class JvmtiCachedClassFieldMap; class MemberNameTable; @@ -235,7 +234,8 @@ class InstanceKlass: public Klass { _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 @@ class InstanceKlass: public Klass { 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* _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; @@ -664,21 +663,31 @@ class InstanceKlass: public Klass { } // 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* 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 @@ -1119,6 +1128,10 @@ private: // 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* old_methods, int emcp_method_count); public: // CDS support - remove and restore oops from metadata. Oops are not shared. virtual void remove_unshareable_info(); @@ -1217,62 +1230,6 @@ class JNIid: public CHeapObj { }; -// 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 { - 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* _prev_EMCP_methods; - -public: - PreviousVersionNode(ConstantPool* prev_constant_pool, - GrowableArray* prev_EMCP_methods); - ~PreviousVersionNode(); - ConstantPool* prev_constant_pool() const { - return _prev_constant_pool; - } - GrowableArray* prev_EMCP_methods() const { - return _prev_EMCP_methods; - } -}; - - -// Helper object for walking previous versions. -class PreviousVersionWalker : public StackObj { - private: - Thread* _thread; - GrowableArray* _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 diff --git a/src/share/vm/oops/klass.cpp b/src/share/vm/oops/klass.cpp index e24a43d4c986d8d47e0bcb16faf6f2841b29b8b0..95a7ca0c8b43f01df45e27a9a20336e52dd20c1c 100644 --- a/src/share/vm/oops/klass.cpp +++ b/src/share/vm/oops/klass.cpp @@ -455,6 +455,12 @@ void Klass::clean_weak_klass_links(BoolObjectClosure* is_alive, bool clean_alive 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); + } } } } diff --git a/src/share/vm/oops/method.cpp b/src/share/vm/oops/method.cpp index 35d55087c61f4cf49c1ed86530f9bcd95b9da65d..0e199cbdb8217480d71e0c394d738f75fca100bf 100644 --- a/src/share/vm/oops/method.cpp +++ b/src/share/vm/oops/method.cpp @@ -91,6 +91,7 @@ Method::Method(ConstMethod* xconst, AccessFlags access_flags, int size) { 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); diff --git a/src/share/vm/oops/method.hpp b/src/share/vm/oops/method.hpp index 90b7029e910661a1ec06fec04d964a1a342c7e91..ac11db4e17c36697b01b5820ac6d60e323678649 100644 --- a/src/share/vm/oops/method.hpp +++ b/src/share/vm/oops/method.hpp @@ -111,6 +111,7 @@ class Method : public Metadata { _caller_sensitive : 1, _force_inline : 1, _hidden : 1, + _running_emcp : 1, _dont_inline : 1, _has_injected_profile : 1, : 2; @@ -711,6 +712,21 @@ class Method : public Metadata { 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); diff --git a/src/share/vm/prims/jvmtiImpl.cpp b/src/share/vm/prims/jvmtiImpl.cpp index 9b308d43f72cb0aa2f2e1e83ad722a884e7887f0..3c66b1671c0236d2a54c40442cb353abe3c9e9c2 100644 --- a/src/share/vm/prims/jvmtiImpl.cpp +++ b/src/share/vm/prims/jvmtiImpl.cpp @@ -282,39 +282,22 @@ address JvmtiBreakpoint::getBcp() { 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* 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* 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)", diff --git a/src/share/vm/prims/jvmtiRedefineClasses.cpp b/src/share/vm/prims/jvmtiRedefineClasses.cpp index f610db2b80120e758045e4156fd3abc17bfb7643..0e194b6f4ea748a45488c2bd40f6fec101467d75 100644 --- a/src/share/vm/prims/jvmtiRedefineClasses.cpp +++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp @@ -3435,13 +3435,12 @@ void VM_RedefineClasses::AdjustCpoolCacheAndVtable::do_klass(Klass* k) { } // 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::update_jmethod_ids() { } } -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 @@ void VM_RedefineClasses::check_methods_and_mark_as_obsolete( // 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 @@ void VM_RedefineClasses::check_methods_and_mark_as_obsolete( 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 @@ void VM_RedefineClasses::redefine_single_class(jclass the_jclass, 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 @@ void VM_RedefineClasses::redefine_single_class(jclass the_jclass, 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); diff --git a/src/share/vm/prims/jvmtiRedefineClasses.hpp b/src/share/vm/prims/jvmtiRedefineClasses.hpp index d2611f2f0018516ddd320c1c2e2ed78cb7f62a9d..aab9d4549190ad6da6b60d91a229f676cb12fe3f 100644 --- a/src/share/vm/prims/jvmtiRedefineClasses.hpp +++ b/src/share/vm/prims/jvmtiRedefineClasses.hpp @@ -403,14 +403,9 @@ class VM_RedefineClasses: public VM_Operation { // 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 diff --git a/test/runtime/RedefineFinalizer/RedefineFinalizer.java b/test/runtime/RedefineTests/RedefineFinalizer.java similarity index 96% rename from test/runtime/RedefineFinalizer/RedefineFinalizer.java rename to test/runtime/RedefineTests/RedefineFinalizer.java index 227b9e8186e7cb59f235d10994d1c3e9d8a7dd62..3d984527b67c593a1dd277b3f564b13cba9ec2ab 100644 --- a/test/runtime/RedefineFinalizer/RedefineFinalizer.java +++ b/test/runtime/RedefineTests/RedefineFinalizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * 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 diff --git a/test/runtime/RedefineTests/RedefineRunningMethods.java b/test/runtime/RedefineTests/RedefineRunningMethods.java new file mode 100644 index 0000000000000000000000000000000000000000..7d44a8a59de79f77da7f1e2ea127b7c513a78379 --- /dev/null +++ b/test/runtime/RedefineTests/RedefineRunningMethods.java @@ -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(); + } + } +}