From fbe19378c38decaff221f6570d1b78bb801a5e5d Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 15 Nov 2023 08:33:44 +0000 Subject: [PATCH] 8319955: Improve dependencies removal during class unloading Reviewed-by: dholmes, eosterlund --- src/hotspot/share/code/dependencyContext.cpp | 43 ++++++++++++++++++-- src/hotspot/share/code/dependencyContext.hpp | 1 + 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/code/dependencyContext.cpp b/src/hotspot/share/code/dependencyContext.cpp index a6697ba366a..d7ce8e92acf 100644 --- a/src/hotspot/share/code/dependencyContext.cpp +++ b/src/hotspot/share/code/dependencyContext.cpp @@ -111,8 +111,7 @@ void DependencyContext::add_dependent_nmethod(nmethod* nm) { } void DependencyContext::release(nmethodBucket* b) { - bool expunge = Atomic::load(&_cleaning_epoch) == 0; - if (expunge) { + if (delete_on_release()) { assert_locked_or_safepoint(CodeCache_lock); delete b; if (UsePerfData) { @@ -178,9 +177,41 @@ nmethodBucket* DependencyContext::release_and_get_next_not_unloading(nmethodBuck // // Invalidate all dependencies in the context void DependencyContext::remove_all_dependents() { - nmethodBucket* b = dependencies_not_unloading(); + // Assume that the dependency is not deleted immediately but moved into the + // purge list when calling this. + assert(!delete_on_release(), "should not delete on release"); + + nmethodBucket* first = Atomic::load_acquire(_dependency_context_addr); + if (first == nullptr) { + return; + } + + nmethodBucket* cur = first; + nmethodBucket* last = cur; + jlong count = 0; + for (; cur != nullptr; cur = cur->next()) { + assert(cur->get_nmethod()->is_unloading(), "must be"); + last = cur; + count++; + } + + // Add the whole list to the purge list at once. + nmethodBucket* old_purge_list_head = Atomic::load(&_purge_list); + for (;;) { + last->set_purge_list_next(old_purge_list_head); + nmethodBucket* next_purge_list_head = Atomic::cmpxchg(&_purge_list, old_purge_list_head, first); + if (old_purge_list_head == next_purge_list_head) { + break; + } + old_purge_list_head = next_purge_list_head; + } + + if (UsePerfData) { + _perf_total_buckets_stale_count->inc(count); + _perf_total_buckets_stale_acc_count->inc(count); + } + set_dependencies(nullptr); - assert(b == nullptr, "All dependents should be unloading"); } void DependencyContext::remove_and_mark_for_deoptimization_all_dependents(DeoptimizationScope* deopt_scope) { @@ -234,6 +265,10 @@ bool DependencyContext::claim_cleanup() { return Atomic::cmpxchg(_last_cleanup_addr, last_cleanup, cleaning_epoch) == last_cleanup; } +bool DependencyContext::delete_on_release() { + return Atomic::load(&_cleaning_epoch) == 0; +} + // Retrieve the first nmethodBucket that has a dependent that does not correspond to // an is_unloading nmethod. Any nmethodBucket entries observed from the original head // that is_unloading() will be unlinked and placed on the purge list. diff --git a/src/hotspot/share/code/dependencyContext.hpp b/src/hotspot/share/code/dependencyContext.hpp index 630311c2e1e..e8d2ac41d0d 100644 --- a/src/hotspot/share/code/dependencyContext.hpp +++ b/src/hotspot/share/code/dependencyContext.hpp @@ -75,6 +75,7 @@ class DependencyContext : public StackObj { volatile uint64_t* _last_cleanup_addr; bool claim_cleanup(); + static bool delete_on_release(); void set_dependencies(nmethodBucket* b); nmethodBucket* dependencies(); nmethodBucket* dependencies_not_unloading();