Skip to content

Commit d999b81

Browse files
shipilevzhengyu123
andcommitted
8331572: Allow using OopMapCache outside of STW GC phases
Co-authored-by: Zhengyu Gu <zgu@openjdk.org> Reviewed-by: coleenp, zgu
1 parent 8291c94 commit d999b81

File tree

10 files changed

+91
-42
lines changed

10 files changed

+91
-42
lines changed

src/hotspot/share/gc/shared/gcVMOperations.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,9 @@ bool VM_GC_Operation::doit_prologue() {
130130

131131

132132
void VM_GC_Operation::doit_epilogue() {
133-
// Clean up old interpreter OopMap entries that were replaced
134-
// during the GC thread root traversal.
135-
OopMapCache::cleanup_old_entries();
133+
// GC thread root traversal likely used OopMapCache a lot, which
134+
// might have created lots of old entries. Trigger the cleanup now.
135+
OopMapCache::trigger_cleanup();
136136
if (Universe::has_reference_pending_list()) {
137137
Heap_lock->notify_all();
138138
}

src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ bool VM_ShenandoahOperation::doit_prologue() {
4242

4343
void VM_ShenandoahOperation::doit_epilogue() {
4444
assert(!ShenandoahHeap::heap()->has_gc_state_changed(), "GC State was not synchronized to java threads.");
45+
// GC thread root traversal likely used OopMapCache a lot, which
46+
// might have created lots of old entries. Trigger the cleanup now.
47+
OopMapCache::trigger_cleanup();
4548
}
4649

4750
bool VM_ShenandoahReferenceOperation::doit_prologue() {
@@ -52,7 +55,6 @@ bool VM_ShenandoahReferenceOperation::doit_prologue() {
5255

5356
void VM_ShenandoahReferenceOperation::doit_epilogue() {
5457
VM_ShenandoahOperation::doit_epilogue();
55-
OopMapCache::cleanup_old_entries();
5658
if (Universe::has_reference_pending_list()) {
5759
Heap_lock->notify_all();
5860
}

src/hotspot/share/gc/x/xDriver.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "gc/x/xServiceability.hpp"
3636
#include "gc/x/xStat.hpp"
3737
#include "gc/x/xVerify.hpp"
38+
#include "interpreter/oopMapCache.hpp"
3839
#include "logging/log.hpp"
3940
#include "memory/universe.hpp"
4041
#include "runtime/threads.hpp"
@@ -130,6 +131,10 @@ class VM_XOperation : public VM_Operation {
130131

131132
virtual void doit_epilogue() {
132133
Heap_lock->unlock();
134+
135+
// GC thread root traversal likely used OopMapCache a lot, which
136+
// might have created lots of old entries. Trigger the cleanup now.
137+
OopMapCache::trigger_cleanup();
133138
}
134139

135140
bool gc_locked() const {

src/hotspot/share/gc/z/zGeneration.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "gc/z/zUncoloredRoot.inline.hpp"
5353
#include "gc/z/zVerify.hpp"
5454
#include "gc/z/zWorkers.hpp"
55+
#include "interpreter/oopMapCache.hpp"
5556
#include "logging/log.hpp"
5657
#include "memory/universe.hpp"
5758
#include "prims/jvmtiTagMap.hpp"
@@ -452,6 +453,10 @@ class VM_ZOperation : public VM_Operation {
452453

453454
virtual void doit_epilogue() {
454455
Heap_lock->unlock();
456+
457+
// GC thread root traversal likely used OopMapCache a lot, which
458+
// might have created lots of old entries. Trigger the cleanup now.
459+
OopMapCache::trigger_cleanup();
455460
}
456461

457462
bool success() const {

src/hotspot/share/interpreter/oopMapCache.cpp

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "runtime/handles.inline.hpp"
3636
#include "runtime/safepoint.hpp"
3737
#include "runtime/signature.hpp"
38+
#include "utilities/globalCounter.inline.hpp"
3839

3940
class OopMapCacheEntry: private InterpreterOopMap {
4041
friend class InterpreterOopMap;
@@ -494,15 +495,11 @@ void OopMapCache::flush_obsolete_entries() {
494495
}
495496
}
496497

497-
// Called by GC for thread root scan during a safepoint only. The other interpreted frame oopmaps
498-
// are generated locally and not cached.
498+
// Lookup or compute/cache the entry.
499499
void OopMapCache::lookup(const methodHandle& method,
500500
int bci,
501501
InterpreterOopMap* entry_for) {
502-
assert(SafepointSynchronize::is_at_safepoint(), "called by GC in a safepoint");
503502
int probe = hash_value_for(method, bci);
504-
int i;
505-
OopMapCacheEntry* entry = nullptr;
506503

507504
if (log_is_enabled(Debug, interpreter, oopmap)) {
508505
static int count = 0;
@@ -512,14 +509,18 @@ void OopMapCache::lookup(const methodHandle& method,
512509
method()->name_and_sig_as_C_string(), probe);
513510
}
514511

515-
// Search hashtable for match
516-
for(i = 0; i < _probe_depth; i++) {
517-
entry = entry_at(probe + i);
518-
if (entry != nullptr && !entry->is_empty() && entry->match(method, bci)) {
519-
entry_for->resource_copy(entry);
520-
assert(!entry_for->is_empty(), "A non-empty oop map should be returned");
521-
log_debug(interpreter, oopmap)("- found at hash %d", probe + i);
522-
return;
512+
// Search hashtable for match.
513+
// Need a critical section to avoid race against concurrent reclamation.
514+
{
515+
GlobalCounter::CriticalSection cs(Thread::current());
516+
for (int i = 0; i < _probe_depth; i++) {
517+
OopMapCacheEntry *entry = entry_at(probe + i);
518+
if (entry != nullptr && !entry->is_empty() && entry->match(method, bci)) {
519+
entry_for->resource_copy(entry);
520+
assert(!entry_for->is_empty(), "A non-empty oop map should be returned");
521+
log_debug(interpreter, oopmap)("- found at hash %d", probe + i);
522+
return;
523+
}
523524
}
524525
}
525526

@@ -541,8 +542,8 @@ void OopMapCache::lookup(const methodHandle& method,
541542
}
542543

543544
// First search for an empty slot
544-
for(i = 0; i < _probe_depth; i++) {
545-
entry = entry_at(probe + i);
545+
for (int i = 0; i < _probe_depth; i++) {
546+
OopMapCacheEntry* entry = entry_at(probe + i);
546547
if (entry == nullptr) {
547548
if (put_at(probe + i, tmp, nullptr)) {
548549
assert(!entry_for->is_empty(), "A non-empty oop map should be returned");
@@ -557,6 +558,10 @@ void OopMapCache::lookup(const methodHandle& method,
557558
// where the first entry in the collision array is replaced with the new one.
558559
OopMapCacheEntry* old = entry_at(probe + 0);
559560
if (put_at(probe + 0, tmp, old)) {
561+
// Cannot deallocate old entry on the spot: it can still be used by readers
562+
// that got a reference to it before we were able to replace it in the map.
563+
// Instead of synchronizing on GlobalCounter here and incurring heavy thread
564+
// walk, we do this clean up out of band.
560565
enqueue_for_cleanup(old);
561566
} else {
562567
OopMapCacheEntry::deallocate(tmp);
@@ -567,13 +572,14 @@ void OopMapCache::lookup(const methodHandle& method,
567572
}
568573

569574
void OopMapCache::enqueue_for_cleanup(OopMapCacheEntry* entry) {
570-
bool success = false;
571-
OopMapCacheEntry* head;
572-
do {
573-
head = _old_entries;
575+
while (true) {
576+
OopMapCacheEntry* head = Atomic::load(&_old_entries);
574577
entry->_next = head;
575-
success = Atomic::cmpxchg(&_old_entries, head, entry) == head;
576-
} while (!success);
578+
if (Atomic::cmpxchg(&_old_entries, head, entry) == head) {
579+
// Enqueued successfully.
580+
break;
581+
}
582+
}
577583

578584
if (log_is_enabled(Debug, interpreter, oopmap)) {
579585
ResourceMark rm;
@@ -582,11 +588,28 @@ void OopMapCache::enqueue_for_cleanup(OopMapCacheEntry* entry) {
582588
}
583589
}
584590

585-
// This is called after GC threads are done and nothing is accessing the old_entries
586-
// list, so no synchronization needed.
587-
void OopMapCache::cleanup_old_entries() {
588-
OopMapCacheEntry* entry = _old_entries;
589-
_old_entries = nullptr;
591+
bool OopMapCache::has_cleanup_work() {
592+
return Atomic::load(&_old_entries) != nullptr;
593+
}
594+
595+
void OopMapCache::trigger_cleanup() {
596+
if (has_cleanup_work()) {
597+
MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
598+
Service_lock->notify_all();
599+
}
600+
}
601+
602+
void OopMapCache::cleanup() {
603+
OopMapCacheEntry* entry = Atomic::xchg(&_old_entries, (OopMapCacheEntry*)nullptr);
604+
if (entry == nullptr) {
605+
// No work.
606+
return;
607+
}
608+
609+
// About to delete the entries than might still be accessed by other threads
610+
// on lookup path. Need to sync up with them before proceeding.
611+
GlobalCounter::write_synchronize();
612+
590613
while (entry != nullptr) {
591614
if (log_is_enabled(Debug, interpreter, oopmap)) {
592615
ResourceMark rm;

src/hotspot/share/interpreter/oopMapCache.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,15 @@ class OopMapCache : public CHeapObj<mtClass> {
179179

180180
// Compute an oop map without updating the cache or grabbing any locks (for debugging)
181181
static void compute_one_oop_map(const methodHandle& method, int bci, InterpreterOopMap* entry);
182-
static void cleanup_old_entries();
182+
183+
// Check if we need to clean up old entries
184+
static bool has_cleanup_work();
185+
186+
// Request cleanup if work is needed
187+
static void trigger_cleanup();
188+
189+
// Clean up the old entries
190+
static void cleanup();
183191
};
184192

185193
#endif // SHARE_INTERPRETER_OOPMAPCACHE_HPP

src/hotspot/share/oops/method.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -310,16 +310,13 @@ int Method::fast_exception_handler_bci_for(const methodHandle& mh, Klass* ex_kla
310310

311311
void Method::mask_for(int bci, InterpreterOopMap* mask) {
312312
methodHandle h_this(Thread::current(), this);
313-
// Only GC uses the OopMapCache during thread stack root scanning
314-
// any other uses generate an oopmap but do not save it in the cache.
315-
if (Universe::heap()->is_stw_gc_active()) {
316-
method_holder()->mask_for(h_this, bci, mask);
317-
} else {
318-
OopMapCache::compute_one_oop_map(h_this, bci, mask);
319-
}
320-
return;
313+
mask_for(h_this, bci, mask);
321314
}
322315

316+
void Method::mask_for(const methodHandle& this_mh, int bci, InterpreterOopMap* mask) {
317+
assert(this_mh() == this, "Sanity");
318+
method_holder()->mask_for(this_mh, bci, mask);
319+
}
323320

324321
int Method::bci_from(address bcp) const {
325322
if (is_native() && bcp == 0) {

src/hotspot/share/oops/method.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,10 @@ class Method : public Metadata {
446446
address signature_handler() const { return *(signature_handler_addr()); }
447447
void set_signature_handler(address handler);
448448

449-
// Interpreter oopmap support
449+
// Interpreter oopmap support.
450+
// If handle is already available, call with it for better performance.
450451
void mask_for(int bci, InterpreterOopMap* mask);
452+
void mask_for(const methodHandle& this_mh, int bci, InterpreterOopMap* mask);
451453

452454
// operations on invocation counter
453455
void print_invocation_count(outputStream* st);

src/hotspot/share/runtime/frame.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ void frame::oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool quer
950950
ResourceMark rm(thread);
951951
InterpreterOopMap mask;
952952
if (query_oop_map_cache) {
953-
m->mask_for(bci, &mask);
953+
m->mask_for(m, bci, &mask);
954954
} else {
955955
OopMapCache::compute_one_oop_map(m, bci, &mask);
956956
}

src/hotspot/share/runtime/serviceThread.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "gc/shared/oopStorage.hpp"
3434
#include "gc/shared/oopStorageSet.hpp"
3535
#include "memory/universe.hpp"
36+
#include "interpreter/oopMapCache.hpp"
3637
#include "oops/oopHandle.inline.hpp"
3738
#include "runtime/handles.inline.hpp"
3839
#include "runtime/interfaceSupport.inline.hpp"
@@ -95,6 +96,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
9596
bool oop_handles_to_release = false;
9697
bool cldg_cleanup_work = false;
9798
bool jvmti_tagmap_work = false;
99+
bool oopmap_cache_work = false;
98100
{
99101
// Need state transition ThreadBlockInVM so that this thread
100102
// will be handled by safepoint correctly when this thread is
@@ -124,7 +126,8 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
124126
(oopstorage_work = OopStorage::has_cleanup_work_and_reset()) |
125127
(oop_handles_to_release = JavaThread::has_oop_handles_to_release()) |
126128
(cldg_cleanup_work = ClassLoaderDataGraph::should_clean_metaspaces_and_reset()) |
127-
(jvmti_tagmap_work = JvmtiTagMap::has_object_free_events_and_reset())
129+
(jvmti_tagmap_work = JvmtiTagMap::has_object_free_events_and_reset()) |
130+
(oopmap_cache_work = OopMapCache::has_cleanup_work())
128131
) == 0) {
129132
// Wait until notified that there is some work to do or timer expires.
130133
// Some cleanup requests don't notify the ServiceThread so work needs to be done at periodic intervals.
@@ -196,6 +199,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
196199
if (jvmti_tagmap_work) {
197200
JvmtiTagMap::flush_all_object_free_events();
198201
}
202+
203+
if (oopmap_cache_work) {
204+
OopMapCache::cleanup();
205+
}
199206
}
200207
}
201208

0 commit comments

Comments
 (0)