diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 99bcc72..c167e0b 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -657,7 +657,7 @@ void CodeCache::blobs_do(void f(CodeBlob* nm)) { void CodeCache::nmethods_do(void f(nmethod* nm)) { assert_locked_or_safepoint(CodeCache_lock); - NMethodIterator iter; + NMethodIterator iter(NMethodIterator::all_blobs); while(iter.next()) { f(iter.method()); } @@ -665,8 +665,8 @@ void CodeCache::nmethods_do(void f(nmethod* nm)) { void CodeCache::metadata_do(void f(Metadata* m)) { assert_locked_or_safepoint(CodeCache_lock); - NMethodIterator iter; - while(iter.next_alive()) { + NMethodIterator iter(NMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { iter.method()->metadata_do(f); } AOTLoader::metadata_do(f); @@ -684,8 +684,8 @@ int CodeCache::alignment_offset() { void CodeCache::do_unloading(BoolObjectClosure* is_alive, bool unloading_occurred) { assert_locked_or_safepoint(CodeCache_lock); UnloadingScope scope(is_alive); - CompiledMethodIterator iter; - while(iter.next_alive()) { + CompiledMethodIterator iter(CompiledMethodIterator::only_alive); + while(iter.next()) { iter.method()->do_unloading(unloading_occurred); } } @@ -842,8 +842,8 @@ void CodeCache::asserted_non_scavengable_nmethods_do(CodeBlobClosure* f) { // Temporarily mark nmethods that are claimed to be on the scavenge list. void CodeCache::mark_scavenge_root_nmethods() { - NMethodIterator iter; - while(iter.next_alive()) { + NMethodIterator iter(NMethodIterator::only_alive); + while(iter.next()) { nmethod* nm = iter.method(); assert(nm->scavenge_root_not_marked(), "clean state"); if (nm->on_scavenge_root_list()) @@ -854,8 +854,8 @@ void CodeCache::mark_scavenge_root_nmethods() { // If the closure is given, run it on the unlisted nmethods. // Also make sure that the effects of mark_scavenge_root_nmethods is gone. void CodeCache::verify_perm_nmethods(CodeBlobClosure* f_or_null) { - NMethodIterator iter; - while(iter.next_alive()) { + NMethodIterator iter(NMethodIterator::only_alive); + while(iter.next()) { nmethod* nm = iter.method(); bool call_f = (f_or_null != NULL); assert(nm->scavenge_root_not_marked(), "must be already processed"); @@ -869,8 +869,8 @@ void CodeCache::verify_perm_nmethods(CodeBlobClosure* f_or_null) { void CodeCache::verify_clean_inline_caches() { #ifdef ASSERT - NMethodIterator iter; - while(iter.next_alive()) { + NMethodIterator iter(NMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { nmethod* nm = iter.method(); assert(!nm->is_unloaded(), "Tautology"); nm->verify_clean_inline_caches(); @@ -943,8 +943,8 @@ void CodeCache::increment_unloading_cycle() { void CodeCache::verify_oops() { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); VerifyOopClosure voc; - NMethodIterator iter; - while(iter.next_alive()) { + NMethodIterator iter(NMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { nmethod* nm = iter.method(); nm->oops_do(&voc); nm->verify_oop_relocations(); @@ -1120,16 +1120,16 @@ int CodeCache::number_of_nmethods_with_dependencies() { void CodeCache::clear_inline_caches() { assert_locked_or_safepoint(CodeCache_lock); - CompiledMethodIterator iter; - while(iter.next_alive()) { + CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { iter.method()->clear_inline_caches(); } } void CodeCache::cleanup_inline_caches() { assert_locked_or_safepoint(CodeCache_lock); - NMethodIterator iter; - while(iter.next_alive()) { + NMethodIterator iter(NMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { iter.method()->cleanup_inline_caches(/*clean_all=*/true); } } @@ -1199,8 +1199,8 @@ int CodeCache::mark_for_evol_deoptimization(InstanceKlass* dependee) { } } - CompiledMethodIterator iter; - while(iter.next_alive()) { + CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { CompiledMethod* nm = iter.method(); if (nm->is_marked_for_deoptimization()) { // ...Already marked in the previous pass; don't count it again. @@ -1222,8 +1222,8 @@ int CodeCache::mark_for_evol_deoptimization(InstanceKlass* dependee) { // Deoptimize all methods void CodeCache::mark_all_nmethods_for_deoptimization() { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - CompiledMethodIterator iter; - while(iter.next_alive()) { + CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { CompiledMethod* nm = iter.method(); if (!nm->method()->is_method_handle_intrinsic()) { nm->mark_for_deoptimization(); @@ -1235,8 +1235,8 @@ int CodeCache::mark_for_deoptimization(Method* dependee) { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); int number_of_marked_CodeBlobs = 0; - CompiledMethodIterator iter; - while(iter.next_alive()) { + CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { CompiledMethod* nm = iter.method(); if (nm->is_dependent_on_method(dependee)) { ResourceMark rm; @@ -1250,8 +1250,8 @@ int CodeCache::mark_for_deoptimization(Method* dependee) { void CodeCache::make_marked_nmethods_not_entrant() { assert_locked_or_safepoint(CodeCache_lock); - CompiledMethodIterator iter; - while(iter.next_alive()) { + CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { CompiledMethod* nm = iter.method(); if (nm->is_marked_for_deoptimization() && !nm->is_not_entrant()) { nm->make_not_entrant(); @@ -1519,7 +1519,7 @@ void CodeCache::print_internals() { int *buckets = NEW_C_HEAP_ARRAY(int, bucketLimit, mtCode); memset(buckets, 0, sizeof(int) * bucketLimit); - NMethodIterator iter; + NMethodIterator iter(NMethodIterator::all_blobs); while(iter.next()) { nmethod* nm = iter.method(); if(nm->method() != NULL && nm->is_java_method()) { @@ -1659,8 +1659,8 @@ void CodeCache::print_summary(outputStream* st, bool detailed) { void CodeCache::print_codelist(outputStream* st) { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - CompiledMethodIterator iter; - while (iter.next_alive()) { + CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + while (iter.next()) { CompiledMethod* cm = iter.method(); ResourceMark rm; char* method_name = cm->method()->name_and_sig_as_C_string(); diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 89704dd..f76f131 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -334,13 +334,21 @@ class CodeCache : AllStatic { // Iterator to iterate over nmethods in the CodeCache. template class CodeBlobIterator : public StackObj { + public: + enum LivenessFilter { all_blobs, only_alive, only_alive_and_not_unloading }; + private: CodeBlob* _code_blob; // Current CodeBlob GrowableArrayIterator _heap; GrowableArrayIterator _end; + bool _only_alive; + bool _only_not_unloading; public: - CodeBlobIterator(T* nm = NULL) { + CodeBlobIterator(LivenessFilter filter, T* nm = NULL) + : _only_alive(filter == only_alive || filter == only_alive_and_not_unloading), + _only_not_unloading(filter == only_alive_and_not_unloading) + { if (Filter::heaps() == NULL) { return; } @@ -356,33 +364,47 @@ template class CodeBlobIterator : public StackObj { } } + CodeBlobIterator(const CodeBlobIterator& other) + : _code_blob(other._code_blob), + _heap(other._heap), + _end(other._end), + _only_alive(other._only_alive), + _only_not_unloading(other._only_not_unloading) + { } + // Advance iterator to next blob bool next() { assert_locked_or_safepoint(CodeCache_lock); - bool result = next_blob(); - while (!result && _heap != _end) { - // Advance to next code heap of segmented code cache - if (++_heap == _end) { - break; + for (;;) { + // Walk through heaps as required + if (!next_blob()) { + if (_heap == _end) { + return false; + } + ++_heap; + continue; } - result = next_blob(); - } - return result; - } + // Filter is_alive as required + if (_only_alive && !_code_blob->is_alive()) { + continue; + } + + // Filter is_unloading as required + if (_only_not_unloading) { + CompiledMethod* cm = _code_blob->as_compiled_method_or_null(); + if (cm != NULL && cm->is_unloading()) { + continue; + } + } - // Advance iterator to next alive blob - bool next_alive() { - bool result = next(); - while(result && !_code_blob->is_alive()) { - result = next(); + return true; } - return result; } - bool end() const { return _code_blob == NULL; } - T* method() const { return (T*)_code_blob; } + bool end() const { return _code_blob == NULL; } + T* method() const { return (T*)_code_blob; } private: @@ -422,7 +444,6 @@ struct NMethodFilter { static const GrowableArray* heaps() { return CodeCache::nmethod_heaps(); } }; - typedef CodeBlobIterator CompiledMethodIterator; typedef CodeBlobIterator NMethodIterator; diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 839ca5b..65ce9a5 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1016,13 +1016,13 @@ void nmethod::mark_as_seen_on_stack() { // there are no activations on the stack, not in use by the VM, // and not in use by the ServiceThread) bool nmethod::can_convert_to_zombie() { - assert(is_not_entrant(), "must be a non-entrant method"); + assert(is_not_entrant() || is_unloading(), "must be a non-entrant method"); // Since the nmethod sweeper only does partial sweep the sweeper's traversal // count can be greater than the stack traversal count before it hits the // nmethod for the second time. - return stack_traversal_mark()+1 < NMethodSweeper::traversal_count() && - !is_locked_by_vm(); + return stack_traversal_mark() + 1 < NMethodSweeper::traversal_count() && + !is_locked_by_vm() && (!is_unloading() || is_unloaded()); } void nmethod::inc_decompile_count() { @@ -1090,8 +1090,6 @@ void nmethod::make_unloaded() { // Unregister must be done before the state change Universe::heap()->unregister_nmethod(this); - _state = unloaded; - // Log the unloading. log_state_change(); @@ -1107,6 +1105,13 @@ void nmethod::make_unloaded() { set_osr_link(NULL); NMethodSweeper::report_state_change(this); + + // The release is only needed for compile-time ordering, as accesses + // into the nmethod after the store is not safe, due to the sweeper + // being allowed to free it when the store is observed, during + // concurrent nmethod unloading. Therefore, there is no need for + // acquire on the loader side. + OrderAccess::release_store(&_state, (signed char)unloaded); } void nmethod::invalidate_osr_method() { @@ -1842,11 +1847,11 @@ void nmethod::check_all_dependencies(DepChange& changes) { // Iterate over live nmethods and check dependencies of all nmethods that are not // marked for deoptimization. A particular dependency is only checked once. - NMethodIterator iter; + NMethodIterator iter(NMethodIterator::only_alive_and_not_unloading); while(iter.next()) { nmethod* nm = iter.method(); // Only notify for live nmethods - if (nm->is_alive() && !nm->is_marked_for_deoptimization()) { + if (!nm->is_marked_for_deoptimization()) { for (Dependencies::DepStream deps(nm); deps.next(); ) { // Construct abstraction of a dependency. DependencySignature* current_sig = new DependencySignature(deps); @@ -2841,7 +2846,7 @@ void nmethod::maybe_invalidate_installed_code() { // Update the values in the InstalledCode instance if it still refers to this nmethod nmethod* nm = (nmethod*)InstalledCode::address(installed_code); if (nm == this) { - if (!is_alive()) { + if (!is_alive() || is_unloading()) { // Break the link between nmethod and InstalledCode such that the nmethod // can subsequently be flushed safely. The link must be maintained while // the method could have live activations since invalidateInstalledCode @@ -2856,7 +2861,7 @@ void nmethod::maybe_invalidate_installed_code() { } } } - if (!is_alive()) { + if (!is_alive() || is_unloading()) { // Clear these out after the nmethod has been unregistered and any // updates to the InstalledCode instance have been performed. clear_jvmci_installed_code(); @@ -2880,7 +2885,7 @@ void nmethod::invalidate_installed_code(Handle installedCode, TRAPS) { { MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag); // This relationship can only be checked safely under a lock - assert(!nm->is_alive() || nm->jvmci_installed_code() == installedCode(), "sanity check"); + assert(!nm->is_alive() || nm->is_unloading() || nm->jvmci_installed_code() == installedCode(), "sanity check"); } #endif diff --git a/src/hotspot/share/gc/shared/parallelCleaning.cpp b/src/hotspot/share/gc/shared/parallelCleaning.cpp index 119892c..725bfbc 100644 --- a/src/hotspot/share/gc/shared/parallelCleaning.cpp +++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp @@ -74,8 +74,8 @@ CodeCacheUnloadingTask::CodeCacheUnloadingTask(uint num_workers, BoolObjectClosu _first_nmethod(NULL), _claimed_nmethod(NULL) { // Get first alive nmethod - CompiledMethodIterator iter = CompiledMethodIterator(); - if(iter.next_alive()) { + CompiledMethodIterator iter(CompiledMethodIterator::only_alive); + if(iter.next()) { _first_nmethod = iter.method(); } _claimed_nmethod = _first_nmethod; @@ -91,18 +91,18 @@ CodeCacheUnloadingTask::~CodeCacheUnloadingTask() { void CodeCacheUnloadingTask::claim_nmethods(CompiledMethod** claimed_nmethods, int *num_claimed_nmethods) { CompiledMethod* first; - CompiledMethodIterator last; + CompiledMethodIterator last(CompiledMethodIterator::only_alive); do { *num_claimed_nmethods = 0; first = _claimed_nmethod; - last = CompiledMethodIterator(first); + last = CompiledMethodIterator(CompiledMethodIterator::only_alive, first); if (first != NULL) { for (int i = 0; i < MaxClaimNmethods; i++) { - if (!last.next_alive()) { + if (!last.next()) { break; } claimed_nmethods[i] = last.method(); diff --git a/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp b/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp index 9b63612..a0e0b5d 100644 --- a/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp +++ b/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp @@ -228,8 +228,8 @@ jvmtiError JvmtiCodeBlobEvents::generate_compiled_method_load_events(JvmtiEnv* e // can be safely skipped. MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); // Iterate over non-profiled and profiled nmethods - NMethodIterator iter; - while(iter.next_alive()) { + NMethodIterator iter(NMethodIterator::only_alive_and_not_unloading); + while(iter.next()) { nmethod* current = iter.method(); // Lock the nmethod so it can't be freed nmethodLocker nml(current); diff --git a/src/hotspot/share/runtime/javaCalls.cpp b/src/hotspot/share/runtime/javaCalls.cpp index 295fca9..c553661 100644 --- a/src/hotspot/share/runtime/javaCalls.cpp +++ b/src/hotspot/share/runtime/javaCalls.cpp @@ -427,7 +427,7 @@ void JavaCalls::call_helper(JavaValue* result, const methodHandle& method, JavaC #if INCLUDE_JVMCI if (alternative_target != NULL) { - if (alternative_target->is_alive()) { + if (alternative_target->is_alive() && !alternative_target->is_unloading()) { thread->set_jvmci_alternate_call_target(alternative_target->verified_entry_point()); entry_point = method->adapter()->get_i2c_entry(); } else { diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 1b67d85..da61a30 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -1279,7 +1279,7 @@ methodHandle SharedRuntime::resolve_sub_helper(JavaThread *thread, (!is_virtual && invoke_code == Bytecodes::_invokedynamic) || ( is_virtual && invoke_code != Bytecodes::_invokestatic ), "inconsistent bytecode"); - assert(caller_nm->is_alive(), "It should be alive"); + assert(caller_nm->is_alive() && !caller_nm->is_unloading(), "It should be alive"); #ifndef PRODUCT // tracing/debugging/statistics @@ -1606,8 +1606,10 @@ methodHandle SharedRuntime::handle_ic_miss_helper(JavaThread *thread, TRAPS) { } else if (inline_cache->is_icholder_call()) { CompiledICHolder* ic_oop = inline_cache->cached_icholder(); if (ic_oop != NULL) { - - if (receiver()->klass() == ic_oop->holder_klass()) { + if (!ic_oop->is_loader_alive()) { + // Deferred IC cleaning due to concurrent class unloading + inline_cache->set_to_clean(); + } else if (receiver()->klass() == ic_oop->holder_klass()) { // This isn't a real miss. We must have seen that compiled code // is now available and we want the call site converted to a // monomorphic compiled call site. diff --git a/src/hotspot/share/runtime/sweeper.cpp b/src/hotspot/share/runtime/sweeper.cpp index 8ed5213..c121f90 100644 --- a/src/hotspot/share/runtime/sweeper.cpp +++ b/src/hotspot/share/runtime/sweeper.cpp @@ -142,7 +142,7 @@ void NMethodSweeper::init_sweeper_log() { #define SWEEP(nm) #endif -CompiledMethodIterator NMethodSweeper::_current; // Current compiled method +CompiledMethodIterator NMethodSweeper::_current(CompiledMethodIterator::all_blobs); // Current compiled method long NMethodSweeper::_traversals = 0; // Stack scan count, also sweep ID. long NMethodSweeper::_total_nof_code_cache_sweeps = 0; // Total number of full sweeps of the code cache long NMethodSweeper::_time_counter = 0; // Virtual time used to periodically invoke sweeper @@ -274,7 +274,7 @@ CodeBlobClosure* NMethodSweeper::prepare_mark_active_nmethods() { assert(wait_for_stack_scanning(), "should only happen between sweeper cycles"); _seen = 0; - _current = CompiledMethodIterator(); + _current = CompiledMethodIterator(CompiledMethodIterator::all_blobs); // Initialize to first nmethod _current.next(); _traversals += 1; @@ -653,7 +653,7 @@ class CompiledMethodMarker: public StackObj { JavaThread* current = JavaThread::current(); assert (current->is_Code_cache_sweeper_thread(), "Must be"); _thread = (CodeCacheSweeperThread*)current; - if (!cm->is_zombie() && !cm->is_unloaded()) { + if (!cm->is_zombie() && !cm->is_unloading()) { // Only expose live nmethods for scanning _thread->set_scanned_compiled_method(cm); } @@ -697,7 +697,7 @@ NMethodSweeper::MethodStateChange NMethodSweeper::process_compiled_method(Compil // Skip methods that are currently referenced by the VM if (cm->is_locked_by_vm()) { // But still remember to clean-up inline caches for alive nmethods - if (cm->is_alive()) { + if (cm->is_alive() && !cm->is_unloading()) { // Clean inline caches that point to zombie/non-entrant/unloaded nmethods CompiledICLocker ml(cm); cm->cleanup_inline_caches(false); @@ -786,7 +786,7 @@ NMethodSweeper::MethodStateChange NMethodSweeper::process_compiled_method(Compil void NMethodSweeper::possibly_flush(nmethod* nm) { if (UseCodeCacheFlushing) { - if (!nm->is_locked_by_vm() && !nm->is_native_method() && !nm->is_not_installed()) { + if (!nm->is_locked_by_vm() && !nm->is_native_method() && !nm->is_not_installed() && !nm->is_unloading()) { bool make_not_entrant = false; // Do not make native methods not-entrant