410 lines
14 KiB
Diff
410 lines
14 KiB
Diff
From 7fa0e2cc00d64ee0399740ff20971f5c11517172 Mon Sep 17 00:00:00 2001
|
|
Date: Fri, 22 Jan 2021 11:36:20 +0800
|
|
Subject: Backport of JDK-8186042 for OopmapCache implementation
|
|
|
|
Summary: Backport of JDK-8186042 for OopmapCache implementation
|
|
LLT:
|
|
Bug url: https://bugs.openjdk.java.net/browse/JDK-8186042
|
|
---
|
|
.../shared/vmGCOperations.cpp | 6 +-
|
|
.../src/share/vm/interpreter/oopMapCache.cpp | 150 +++++++++++-------
|
|
.../src/share/vm/interpreter/oopMapCache.hpp | 11 +-
|
|
hotspot/src/share/vm/oops/method.cpp | 26 +--
|
|
hotspot/src/share/vm/runtime/memprofiler.cpp | 2 +-
|
|
hotspot/src/share/vm/runtime/vframe.cpp | 8 +-
|
|
6 files changed, 111 insertions(+), 92 deletions(-)
|
|
|
|
diff --git a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp
|
|
index d60f751af..85059b82f 100644
|
|
--- a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp
|
|
+++ b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp
|
|
@@ -40,6 +40,7 @@
|
|
#if INCLUDE_ALL_GCS
|
|
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
|
|
#endif // INCLUDE_ALL_GCS
|
|
+#include "interpreter/oopMapCache.hpp"
|
|
|
|
#ifndef USDT2
|
|
HS_DTRACE_PROBE_DECL1(hotspot, gc__begin, bool);
|
|
@@ -134,7 +135,10 @@ bool VM_GC_Operation::doit_prologue() {
|
|
|
|
void VM_GC_Operation::doit_epilogue() {
|
|
assert(Thread::current()->is_Java_thread(), "just checking");
|
|
- // Release the Heap_lock first.
|
|
+ // Clean up old interpreter OopMap entries that were replaced
|
|
+ // during the GC thread root traversal.
|
|
+ OopMapCache::cleanup_old_entries();
|
|
+ // Release the Heap_lock.
|
|
SharedHeap* sh = SharedHeap::heap();
|
|
if (sh != NULL) sh->_thread_holds_heap_lock_for_gc = false;
|
|
Heap_lock->unlock();
|
|
diff --git a/hotspot/src/share/vm/interpreter/oopMapCache.cpp b/hotspot/src/share/vm/interpreter/oopMapCache.cpp
|
|
index f696bcb25..528906267 100644
|
|
--- a/hotspot/src/share/vm/interpreter/oopMapCache.cpp
|
|
+++ b/hotspot/src/share/vm/interpreter/oopMapCache.cpp
|
|
@@ -30,6 +30,7 @@
|
|
#include "prims/jvmtiRedefineClassesTrace.hpp"
|
|
#include "runtime/handles.inline.hpp"
|
|
#include "runtime/signature.hpp"
|
|
+#include "runtime/atomic.inline.hpp"
|
|
|
|
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
|
|
|
|
@@ -39,6 +40,9 @@ class OopMapCacheEntry: private InterpreterOopMap {
|
|
friend class OopMapCache;
|
|
friend class VerifyClosure;
|
|
|
|
+ private:
|
|
+ OopMapCacheEntry* _next;
|
|
+
|
|
protected:
|
|
// Initialization
|
|
void fill(methodHandle method, int bci);
|
|
@@ -56,8 +60,9 @@ class OopMapCacheEntry: private InterpreterOopMap {
|
|
|
|
public:
|
|
OopMapCacheEntry() : InterpreterOopMap() {
|
|
+ _next = NULL;
|
|
#ifdef ASSERT
|
|
- _resource_allocate_bit_mask = false;
|
|
+ _resource_allocate_bit_mask = false;
|
|
#endif
|
|
}
|
|
};
|
|
@@ -424,16 +429,6 @@ void OopMapCacheEntry::flush() {
|
|
|
|
// Implementation of OopMapCache
|
|
|
|
-#ifndef PRODUCT
|
|
-
|
|
-static size_t _total_memory_usage = 0;
|
|
-
|
|
-size_t OopMapCache::memory_usage() {
|
|
- return _total_memory_usage;
|
|
-}
|
|
-
|
|
-#endif
|
|
-
|
|
void InterpreterOopMap::resource_copy(OopMapCacheEntry* from) {
|
|
assert(_resource_allocate_bit_mask,
|
|
"Should not resource allocate the _bit_mask");
|
|
@@ -474,15 +469,11 @@ inline unsigned int OopMapCache::hash_value_for(methodHandle method, int bci) co
|
|
^ ((unsigned int) method->size_of_parameters() << 6);
|
|
}
|
|
|
|
+OopMapCacheEntry* volatile OopMapCache::_old_entries = NULL;
|
|
|
|
-OopMapCache::OopMapCache() :
|
|
- _mut(Mutex::leaf, "An OopMapCache lock", true)
|
|
-{
|
|
- _array = NEW_C_HEAP_ARRAY(OopMapCacheEntry, _size, mtClass);
|
|
- // Cannot call flush for initialization, since flush
|
|
- // will check if memory should be deallocated
|
|
- for(int i = 0; i < _size; i++) _array[i].initialize();
|
|
- NOT_PRODUCT(_total_memory_usage += sizeof(OopMapCache) + (sizeof(OopMapCacheEntry) * _size);)
|
|
+OopMapCache::OopMapCache() {
|
|
+ _array = NEW_C_HEAP_ARRAY(OopMapCacheEntry*, _size, mtClass);
|
|
+ for(int i = 0; i < _size; i++) _array[i] = NULL;
|
|
}
|
|
|
|
|
|
@@ -491,44 +482,59 @@ OopMapCache::~OopMapCache() {
|
|
// Deallocate oop maps that are allocated out-of-line
|
|
flush();
|
|
// Deallocate array
|
|
- NOT_PRODUCT(_total_memory_usage -= sizeof(OopMapCache) + (sizeof(OopMapCacheEntry) * _size);)
|
|
- FREE_C_HEAP_ARRAY(OopMapCacheEntry, _array, mtClass);
|
|
+ FREE_C_HEAP_ARRAY(OopMapCacheEntry*, _array, mtClass);
|
|
}
|
|
|
|
OopMapCacheEntry* OopMapCache::entry_at(int i) const {
|
|
- return &_array[i % _size];
|
|
+ return (OopMapCacheEntry*)OrderAccess::load_ptr_acquire(&(_array[i % _size]));
|
|
}
|
|
|
|
+bool OopMapCache::put_at(int i, OopMapCacheEntry* entry, OopMapCacheEntry* old) {
|
|
+ return Atomic::cmpxchg_ptr(entry, &_array[i % _size], old) == old;
|
|
+ }
|
|
void OopMapCache::flush() {
|
|
- for (int i = 0; i < _size; i++) _array[i].flush();
|
|
+ for (int i = 0; i < _size; i++) {
|
|
+ OopMapCacheEntry* entry = _array[i];
|
|
+ if (entry != NULL) {
|
|
+ _array[i] = NULL; // no barrier, only called in OopMapCache destructor
|
|
+ entry->flush();
|
|
+ FREE_C_HEAP_OBJ(entry, mtClass);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
+
|
|
void OopMapCache::flush_obsolete_entries() {
|
|
- for (int i = 0; i < _size; i++)
|
|
- if (!_array[i].is_empty() && _array[i].method()->is_old()) {
|
|
+ assert(SafepointSynchronize::is_at_safepoint(), "called by RedefineClasses in a safepoint");
|
|
+ for (int i = 0; i < _size; i++) {
|
|
+ OopMapCacheEntry* entry = _array[i];
|
|
+ if (entry != NULL && !entry->is_empty() && entry->method()->is_old()) {
|
|
// Cache entry is occupied by an old redefined method and we don't want
|
|
// to pin it down so flush the entry.
|
|
+
|
|
RC_TRACE(0x08000000, ("flush: %s(%s): cached entry @%d",
|
|
- _array[i].method()->name()->as_C_string(),
|
|
- _array[i].method()->signature()->as_C_string(), i));
|
|
+ entry->method()->name()->as_C_string(),
|
|
+ entry->method()->signature()->as_C_string(), i));
|
|
|
|
- _array[i].flush();
|
|
+ _array[i] = NULL;
|
|
+ entry->flush();
|
|
+ FREE_C_HEAP_OBJ(entry, mtClass);
|
|
}
|
|
+ }
|
|
}
|
|
|
|
void OopMapCache::lookup(methodHandle method,
|
|
int bci,
|
|
- InterpreterOopMap* entry_for) const {
|
|
- MutexLocker x(&_mut);
|
|
-
|
|
- OopMapCacheEntry* entry = NULL;
|
|
+ InterpreterOopMap* entry_for){
|
|
+ assert(SafepointSynchronize::is_at_safepoint(), "called by GC in a safepoint");
|
|
int probe = hash_value_for(method, bci);
|
|
+ int i;
|
|
+ OopMapCacheEntry* entry = NULL;
|
|
|
|
// Search hashtable for match
|
|
- int i;
|
|
for(i = 0; i < _probe_depth; i++) {
|
|
entry = entry_at(probe + i);
|
|
- if (entry->match(method, bci)) {
|
|
+ if (entry != NULL && !entry->is_empty() && entry->match(method, bci)) {
|
|
entry_for->resource_copy(entry);
|
|
assert(!entry_for->is_empty(), "A non-empty oop map should be returned");
|
|
return;
|
|
@@ -543,26 +549,31 @@ void OopMapCache::lookup(methodHandle method,
|
|
}
|
|
|
|
// Entry is not in hashtable.
|
|
- // Compute entry and return it
|
|
+ // Compute entry
|
|
+
|
|
+ OopMapCacheEntry* tmp = NEW_C_HEAP_OBJ(OopMapCacheEntry, mtClass);
|
|
+ tmp->initialize();
|
|
+ tmp->fill(method, bci);
|
|
+ entry_for->resource_copy(tmp);
|
|
|
|
if (method->should_not_be_cached()) {
|
|
// It is either not safe or not a good idea to cache this Method*
|
|
// at this time. We give the caller of lookup() a copy of the
|
|
// interesting info via parameter entry_for, but we don't add it to
|
|
// the cache. See the gory details in Method*.cpp.
|
|
- compute_one_oop_map(method, bci, entry_for);
|
|
+ FREE_C_HEAP_OBJ(tmp, mtClass);
|
|
return;
|
|
}
|
|
|
|
// First search for an empty slot
|
|
for(i = 0; i < _probe_depth; i++) {
|
|
- entry = entry_at(probe + i);
|
|
- if (entry->is_empty()) {
|
|
- entry->fill(method, bci);
|
|
- entry_for->resource_copy(entry);
|
|
- assert(!entry_for->is_empty(), "A non-empty oop map should be returned");
|
|
- return;
|
|
- }
|
|
+ entry = entry_at(probe + i);
|
|
+ if (entry == NULL) {
|
|
+ if(put_at(probe + i, tmp, NULL)) {
|
|
+ assert(!entry_for->is_empty(), "A non-empty oop map should be returned");
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
if (TraceOopMapGeneration) {
|
|
@@ -571,30 +582,51 @@ void OopMapCache::lookup(methodHandle method,
|
|
}
|
|
|
|
// No empty slot (uncommon case). Use (some approximation of a) LRU algorithm
|
|
- //entry_at(probe + _probe_depth - 1)->flush();
|
|
- //for(i = _probe_depth - 1; i > 0; i--) {
|
|
- // // Coping entry[i] = entry[i-1];
|
|
- // OopMapCacheEntry *to = entry_at(probe + i);
|
|
- // OopMapCacheEntry *from = entry_at(probe + i - 1);
|
|
- // to->copy(from);
|
|
- // }
|
|
+ // where the first entry in the collision array is replaced with the new one.
|
|
+ OopMapCacheEntry* old = entry_at(probe + 0);
|
|
+ if (put_at(probe + 0, tmp, old)) {
|
|
+ enqueue_for_cleanup(old);
|
|
+ } else {
|
|
+ enqueue_for_cleanup(tmp);
|
|
+ }
|
|
|
|
- assert(method->is_method(), "gaga");
|
|
+ assert(!entry_for->is_empty(), "A non-empty oop map should be returned");
|
|
|
|
- entry = entry_at(probe + 0);
|
|
- entry->fill(method, bci);
|
|
+ return;
|
|
+}
|
|
|
|
- // Copy the newly cached entry to input parameter
|
|
- entry_for->resource_copy(entry);
|
|
+void OopMapCache::enqueue_for_cleanup(OopMapCacheEntry* entry) {
|
|
+ bool success = false;
|
|
+ OopMapCacheEntry* head;
|
|
+ do {
|
|
+ head = _old_entries;
|
|
+ entry->_next = head;
|
|
+ success = Atomic::cmpxchg_ptr((intptr_t*)entry, (intptr_t*)&_old_entries, (intptr_t*)head) == (intptr_t*)head;
|
|
+ } while (!success);
|
|
|
|
if (TraceOopMapGeneration) {
|
|
ResourceMark rm;
|
|
- tty->print("Done with ");
|
|
- method->print_value(); tty->cr();
|
|
+ tty->print_cr("enqueue %s at bci %d for cleanup",
|
|
+ entry->method()->name_and_sig_as_C_string(), entry->bci());
|
|
}
|
|
- assert(!entry_for->is_empty(), "A non-empty oop map should be returned");
|
|
+}
|
|
|
|
- return;
|
|
+// This is called after GC threads are done and nothing is accessing the old_entries
|
|
+// list, so no synchronization needed.
|
|
+void OopMapCache::cleanup_old_entries() {
|
|
+ OopMapCacheEntry* entry = _old_entries;
|
|
+ _old_entries = NULL;
|
|
+ while (entry != NULL) {
|
|
+ if (TraceOopMapGeneration) {
|
|
+ ResourceMark rm;
|
|
+ tty->print_cr("cleanup entry %s at bci %d",
|
|
+ entry->method()->name_and_sig_as_C_string(), entry->bci());
|
|
+ }
|
|
+ OopMapCacheEntry* next = entry->_next;
|
|
+ entry->flush();
|
|
+ FREE_C_HEAP_OBJ(entry, mtClass);
|
|
+ entry = next;
|
|
+ }
|
|
}
|
|
|
|
void OopMapCache::compute_one_oop_map(methodHandle method, int bci, InterpreterOopMap* entry) {
|
|
diff --git a/hotspot/src/share/vm/interpreter/oopMapCache.hpp b/hotspot/src/share/vm/interpreter/oopMapCache.hpp
|
|
index 99fbe8168..ecbe4340a 100644
|
|
--- a/hotspot/src/share/vm/interpreter/oopMapCache.hpp
|
|
+++ b/hotspot/src/share/vm/interpreter/oopMapCache.hpp
|
|
@@ -147,17 +147,19 @@ class InterpreterOopMap: ResourceObj {
|
|
};
|
|
|
|
class OopMapCache : public CHeapObj<mtClass> {
|
|
+ static OopMapCacheEntry* volatile _old_entries;
|
|
private:
|
|
enum { _size = 32, // Use fixed size for now
|
|
_probe_depth = 3 // probe depth in case of collisions
|
|
};
|
|
|
|
- OopMapCacheEntry* _array;
|
|
+ OopMapCacheEntry* volatile * _array;
|
|
|
|
unsigned int hash_value_for(methodHandle method, int bci) const;
|
|
OopMapCacheEntry* entry_at(int i) const;
|
|
|
|
- mutable Mutex _mut;
|
|
+ bool put_at(int i, OopMapCacheEntry* entry, OopMapCacheEntry* old);
|
|
+ static void enqueue_for_cleanup(OopMapCacheEntry* entry);
|
|
|
|
void flush();
|
|
|
|
@@ -170,13 +172,12 @@ class OopMapCache : public CHeapObj<mtClass> {
|
|
|
|
// Returns the oopMap for (method, bci) in parameter "entry".
|
|
// Returns false if an oop map was not found.
|
|
- void lookup(methodHandle method, int bci, InterpreterOopMap* entry) const;
|
|
+ void lookup(methodHandle method, int bci, InterpreterOopMap* entry);
|
|
|
|
// Compute an oop map without updating the cache or grabbing any locks (for debugging)
|
|
static void compute_one_oop_map(methodHandle method, int bci, InterpreterOopMap* entry);
|
|
|
|
- // Returns total no. of bytes allocated as part of OopMapCache's
|
|
- static size_t memory_usage() PRODUCT_RETURN0;
|
|
+ static void cleanup_old_entries();
|
|
};
|
|
|
|
#endif // SHARE_VM_INTERPRETER_OOPMAPCACHE_HPP
|
|
diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp
|
|
index 9e58c0126..24fae4d30 100644
|
|
--- a/hotspot/src/share/vm/oops/method.cpp
|
|
+++ b/hotspot/src/share/vm/oops/method.cpp
|
|
@@ -216,26 +216,14 @@ int Method::fast_exception_handler_bci_for(methodHandle mh, KlassHandle ex_klass
|
|
}
|
|
|
|
void Method::mask_for(int bci, InterpreterOopMap* mask) {
|
|
-
|
|
- Thread* myThread = Thread::current();
|
|
- methodHandle h_this(myThread, this);
|
|
-#ifdef ASSERT
|
|
- bool has_capability = myThread->is_VM_thread() ||
|
|
- myThread->is_ConcurrentGC_thread() ||
|
|
- myThread->is_GC_task_thread();
|
|
-
|
|
- if (!has_capability) {
|
|
- if (!VerifyStack && !VerifyLastFrame) {
|
|
- // verify stack calls this outside VM thread
|
|
- warning("oopmap should only be accessed by the "
|
|
- "VM, GC task or CMS threads (or during debugging)");
|
|
- InterpreterOopMap local_mask;
|
|
- method_holder()->mask_for(h_this, bci, &local_mask);
|
|
- local_mask.print();
|
|
- }
|
|
+ methodHandle h_this(Thread::current(), this);
|
|
+ // Only GC uses the OopMapCache during thread stack root scanning
|
|
+ // any other uses generate an oopmap but do not save it in the cache.
|
|
+ if (Universe::heap()->is_gc_active()) {
|
|
+ method_holder()->mask_for(h_this, bci, mask);
|
|
+ } else {
|
|
+ OopMapCache::compute_one_oop_map(h_this, bci, mask);
|
|
}
|
|
-#endif
|
|
- method_holder()->mask_for(h_this, bci, mask);
|
|
return;
|
|
}
|
|
|
|
diff --git a/hotspot/src/share/vm/runtime/memprofiler.cpp b/hotspot/src/share/vm/runtime/memprofiler.cpp
|
|
index c1cfb60bd..ddb22601f 100644
|
|
--- a/hotspot/src/share/vm/runtime/memprofiler.cpp
|
|
+++ b/hotspot/src/share/vm/runtime/memprofiler.cpp
|
|
@@ -129,7 +129,7 @@ void MemProfiler::do_trace() {
|
|
fprintf(_log_fp, UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) "\n",
|
|
handles_memory_usage / K,
|
|
resource_memory_usage / K,
|
|
- OopMapCache::memory_usage() / K);
|
|
+ 0L);
|
|
fflush(_log_fp);
|
|
}
|
|
|
|
diff --git a/hotspot/src/share/vm/runtime/vframe.cpp b/hotspot/src/share/vm/runtime/vframe.cpp
|
|
index 0d5524118..b3a6d0770 100644
|
|
--- a/hotspot/src/share/vm/runtime/vframe.cpp
|
|
+++ b/hotspot/src/share/vm/runtime/vframe.cpp
|
|
@@ -365,13 +365,7 @@ StackValueCollection* interpretedVFrame::expressions() const {
|
|
StackValueCollection* interpretedVFrame::stack_data(bool expressions) const {
|
|
|
|
InterpreterOopMap oop_mask;
|
|
- // oopmap for current bci
|
|
- if (TraceDeoptimization && Verbose) {
|
|
- methodHandle m_h(Thread::current(), method());
|
|
- OopMapCache::compute_one_oop_map(m_h, bci(), &oop_mask);
|
|
- } else {
|
|
- method()->mask_for(bci(), &oop_mask);
|
|
- }
|
|
+ method()->mask_for(bci(), &oop_mask);
|
|
|
|
const int mask_len = oop_mask.number_of_entries();
|
|
|
|
--
|
|
2.19.0
|
|
|