diff --git a/.hgtags b/.hgtags index aacc44b5c032a24c488f1dd98579a1b79be381e3..e7107d33956a2bb3f65c76008bd1f3b67be38a07 100644 --- a/.hgtags +++ b/.hgtags @@ -1131,6 +1131,7 @@ c3618e1cdefdda6c262f082791bfd988e0e9d9c9 jdk8u162-b10 39e2895b795aded8b584626fb019d35f12e9d1e7 jdk8u162-b11 69aec2ca5d905dde1d0f29a89076d02a531808a3 jdk8u162-b12 caac74fe3cfa9a8c859c28c97d1046a58252af27 jdk8u162-b31 +c9b7abadf150328d2187de05b9e8a9cba2486e47 jdk8u162-b32 a17bab9405474602b18cd62e060a09b17d6413ac jdk8u171-b00 ebfd57cc21e6b7f0c22b17c666b6b28c9340e207 jdk8u171-b01 1acd7c1b80241def8fac90f70b0df16356adad47 jdk8u171-b02 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 6f5f527c8e9f21b17f2b2aa1e1c134be555efa80..fcaaa29f924a0e02f537d49ecd0f6e4d0ebf2d49 100644 --- a/src/share/vm/code/nmethod.cpp +++ b/src/share/vm/code/nmethod.cpp @@ -2172,7 +2172,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 @@ -2198,7 +2198,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 b1c09f633bc4c6960df36a9a75066c85307b12c4..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,205 +3601,126 @@ 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 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)); } -} - -// 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); + // 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) { + if (methods->at(index2)->method_data() != NULL) { + methods->at(index2)->method_data()->clean_weak_method_links(); } - // 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() && @@ -3816,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; } } @@ -3831,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) { @@ -3843,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; @@ -3924,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 f21d8d8aeea053b14420a23afafeb42a81f81913..a5f2eb346cb3e798c465a2d5fae67af811aa14f5 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; @@ -669,21 +668,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 @@ -1124,6 +1133,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(); @@ -1222,62 +1235,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 4f577e5452832b80b1e1c7fe9e47741adab69b99..fdc93bc1969d444c45f1a0103a89e35fc65e15fd 100644 --- a/src/share/vm/oops/klass.cpp +++ b/src/share/vm/oops/klass.cpp @@ -468,6 +468,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 2d6f0251acfe675f94456410e8afd02c3d20eadf..fc6f4339a1812536982838c50f14c568a5350e22 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; @@ -712,6 +713,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/oops/methodData.cpp b/src/share/vm/oops/methodData.cpp index f8ec09b26702359306bd99962714cc1c84e8ffc0..2c78cc1d530905cbd0adbc5f89025c1b367e8190 100644 --- a/src/share/vm/oops/methodData.cpp +++ b/src/share/vm/oops/methodData.cpp @@ -1559,9 +1559,35 @@ void MethodData::clean_extra_data_helper(DataLayout* dp, int shift, bool reset) } } -// Remove SpeculativeTrapData entries that reference an unloaded -// method -void MethodData::clean_extra_data(BoolObjectClosure* is_alive) { +class CleanExtraDataClosure : public StackObj { +public: + virtual bool is_live(Method* m) = 0; +}; + +// Check for entries that reference an unloaded method +class CleanExtraDataKlassClosure : public CleanExtraDataClosure { +private: + BoolObjectClosure* _is_alive; +public: + CleanExtraDataKlassClosure(BoolObjectClosure* is_alive) : _is_alive(is_alive) {} + bool is_live(Method* m) { + return m->method_holder()->is_loader_alive(_is_alive); + } +}; + +// Check for entries that reference a redefined method +class CleanExtraDataMethodClosure : public CleanExtraDataClosure { +public: + CleanExtraDataMethodClosure() {} + bool is_live(Method* m) { + return m->on_stack(); + } +}; + + +// Remove SpeculativeTrapData entries that reference an unloaded or +// redefined method +void MethodData::clean_extra_data(CleanExtraDataClosure* cl) { DataLayout* dp = extra_data_base(); DataLayout* end = extra_data_limit(); @@ -1572,7 +1598,7 @@ void MethodData::clean_extra_data(BoolObjectClosure* is_alive) { SpeculativeTrapData* data = new SpeculativeTrapData(dp); Method* m = data->method(); assert(m != NULL, "should have a method"); - if (!m->method_holder()->is_loader_alive(is_alive)) { + if (!cl->is_live(m)) { // "shift" accumulates the number of cells for dead // SpeculativeTrapData entries that have been seen so // far. Following entries must be shifted left by that many @@ -1603,9 +1629,9 @@ void MethodData::clean_extra_data(BoolObjectClosure* is_alive) { } } -// Verify there's no unloaded method referenced by a +// Verify there's no unloaded or redefined method referenced by a // SpeculativeTrapData entry -void MethodData::verify_extra_data_clean(BoolObjectClosure* is_alive) { +void MethodData::verify_extra_data_clean(CleanExtraDataClosure* cl) { #ifdef ASSERT DataLayout* dp = extra_data_base(); DataLayout* end = extra_data_limit(); @@ -1615,7 +1641,7 @@ void MethodData::verify_extra_data_clean(BoolObjectClosure* is_alive) { case DataLayout::speculative_trap_data_tag: { SpeculativeTrapData* data = new SpeculativeTrapData(dp); Method* m = data->method(); - assert(m != NULL && m->method_holder()->is_loader_alive(is_alive), "Method should exist"); + assert(m != NULL && cl->is_live(m), "Method should exist"); break; } case DataLayout::bit_data_tag: @@ -1641,6 +1667,19 @@ void MethodData::clean_method_data(BoolObjectClosure* is_alive) { parameters->clean_weak_klass_links(is_alive); } - clean_extra_data(is_alive); - verify_extra_data_clean(is_alive); + CleanExtraDataKlassClosure cl(is_alive); + clean_extra_data(&cl); + verify_extra_data_clean(&cl); +} + +void MethodData::clean_weak_method_links() { + for (ProfileData* data = first_data(); + is_valid(data); + data = next_data(data)) { + data->clean_weak_method_links(); + } + + CleanExtraDataMethodClosure cl; + clean_extra_data(&cl); + verify_extra_data_clean(&cl); } diff --git a/src/share/vm/oops/methodData.hpp b/src/share/vm/oops/methodData.hpp index dd4ca1fcb5befafa86136b40289c427247e60fb3..3cd7cd6f13993686d933e830af684719842169ac 100644 --- a/src/share/vm/oops/methodData.hpp +++ b/src/share/vm/oops/methodData.hpp @@ -251,6 +251,9 @@ public: // GC support void clean_weak_klass_links(BoolObjectClosure* cl); + + // Redefinition support + void clean_weak_method_links(); }; @@ -508,6 +511,9 @@ public: // GC support virtual void clean_weak_klass_links(BoolObjectClosure* is_alive_closure) {} + // Redefinition support + virtual void clean_weak_method_links() {} + // CI translation: ProfileData can represent both MethodDataOop data // as well as CIMethodData data. This function is provided for translating // an oop in a ProfileData to the ci equivalent. Generally speaking, @@ -2030,6 +2036,7 @@ public: // CC_INTERP_ONLY(class BytecodeInterpreter;) +class CleanExtraDataClosure; class MethodData : public Metadata { friend class VMStructs; @@ -2183,9 +2190,9 @@ private: static bool profile_parameters_jsr292_only(); static bool profile_all_parameters(); - void clean_extra_data(BoolObjectClosure* is_alive); + void clean_extra_data(CleanExtraDataClosure* cl); void clean_extra_data_helper(DataLayout* dp, int shift, bool reset = false); - void verify_extra_data_clean(BoolObjectClosure* is_alive); + void verify_extra_data_clean(CleanExtraDataClosure* cl); public: static int header_size() { @@ -2477,6 +2484,8 @@ public: static bool profile_return_jsr292_only(); void clean_method_data(BoolObjectClosure* is_alive); + + void clean_weak_method_links(); }; #endif // SHARE_VM_OOPS_METHODDATAOOP_HPP 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/compiler/profiling/spectrapredefineclass/Agent.java b/test/compiler/profiling/spectrapredefineclass/Agent.java new file mode 100644 index 0000000000000000000000000000000000000000..42270469512ea7428bbadfd9d3d52d4876cac7fe --- /dev/null +++ b/test/compiler/profiling/spectrapredefineclass/Agent.java @@ -0,0 +1,142 @@ +/* + * 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. + */ + +import java.security.*; +import java.lang.instrument.*; +import java.lang.reflect.*; +import java.lang.management.ManagementFactory; +import com.sun.tools.attach.VirtualMachine; + +class A { + void m() { + } +} + +class B extends A { + void m() { + } +} + +class C extends A { + void m() { + } +} + +class Test { + + static public void m() throws Exception { + for (int i = 0; i < 20000; i++) { + m1(a); + } + for (int i = 0; i < 4; i++) { + m1(b); + } + } + + static boolean m1(A a) { + boolean res = Agent.m2(a); + return res; + } + + static public A a = new A(); + static public B b = new B(); + static public C c = new C(); +} + +public class Agent implements ClassFileTransformer { + + + static class MemoryChunk { + MemoryChunk other; + long[] array; + MemoryChunk(MemoryChunk other) { + other = other; + array = new long[1024 * 1024 * 1024]; + } + } + + static public boolean m2(A a) { + boolean res = false; + if (a.getClass() == B.class) { + a.m(); + } else { + res = true; + } + return res; + } + + static public void main(String[] args) throws Exception { + // Create speculative trap entries + Test.m(); + + String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName(); + int p = nameOfRunningVM.indexOf('@'); + String pid = nameOfRunningVM.substring(0, p); + + // Make the nmethod go away + for (int i = 0; i < 10; i++) { + System.gc(); + } + + // Redefine class + try { + VirtualMachine vm = VirtualMachine.attach(pid); + vm.loadAgent(System.getProperty("test.classes",".") + "/agent.jar", ""); + vm.detach(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + Test.m(); + // GC will hit dead method pointer + for (int i = 0; i < 10; i++) { + System.gc(); + } + } + + public synchronized byte[] transform(final ClassLoader classLoader, + final String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) { + System.out.println("Transforming class " + className); + return classfileBuffer; + } + + public static void redefine(String agentArgs, Instrumentation instrumentation, Class to_redefine) { + + try { + instrumentation.retransformClasses(to_redefine); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public static void agentmain(String agentArgs, Instrumentation instrumentation) throws Exception { + Agent transformer = new Agent(); + instrumentation.addTransformer(transformer, true); + + redefine(agentArgs, instrumentation, Test.class); + } +} diff --git a/test/compiler/profiling/spectrapredefineclass/Launcher.java b/test/compiler/profiling/spectrapredefineclass/Launcher.java new file mode 100644 index 0000000000000000000000000000000000000000..9142738d962fb2cb98afabf711f1410b155e44da --- /dev/null +++ b/test/compiler/profiling/spectrapredefineclass/Launcher.java @@ -0,0 +1,47 @@ +/* + * 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. + */ +import java.io.PrintWriter; +import com.oracle.java.testlibrary.*; + +/* + * @test + * @bug 8038636 + * @library /testlibrary + * @build Agent + * @run main ClassFileInstaller Agent + * @run main Launcher + * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:TypeProfileLevel=222 -Xmx1M -XX:ReservedCodeCacheSize=3M Agent + */ +public class Launcher { + public static void main(String[] args) throws Exception { + + PrintWriter pw = new PrintWriter("MANIFEST.MF"); + pw.println("Agent-Class: Agent"); + pw.println("Can-Retransform-Classes: true"); + pw.close(); + + ProcessBuilder pb = new ProcessBuilder(); + pb.command(new String[] { JDKToolFinder.getJDKTool("jar"), "cmf", "MANIFEST.MF", System.getProperty("test.classes",".") + "/agent.jar", "Agent.class"}); + pb.start().waitFor(); + } +} 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(); + } + } +}