mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-15 16:44:36 +02:00
8221360: Eliminate Shared_DirtyCardQ_lock
Reviewed-by: tschatzl, iwalulya
This commit is contained in:
parent
928b9724c9
commit
0597cde87d
9 changed files with 28 additions and 158 deletions
|
@ -58,8 +58,7 @@ G1BarrierSet::G1BarrierSet(G1CardTable* card_table) :
|
||||||
_satb_mark_queue_buffer_allocator("SATB Buffer Allocator", G1SATBBufferSize),
|
_satb_mark_queue_buffer_allocator("SATB Buffer Allocator", G1SATBBufferSize),
|
||||||
_dirty_card_queue_buffer_allocator("DC Buffer Allocator", G1UpdateBufferSize),
|
_dirty_card_queue_buffer_allocator("DC Buffer Allocator", G1UpdateBufferSize),
|
||||||
_satb_mark_queue_set(&_satb_mark_queue_buffer_allocator),
|
_satb_mark_queue_set(&_satb_mark_queue_buffer_allocator),
|
||||||
_dirty_card_queue_set(&_dirty_card_queue_buffer_allocator),
|
_dirty_card_queue_set(&_dirty_card_queue_buffer_allocator)
|
||||||
_shared_dirty_card_queue(&_dirty_card_queue_set)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void G1BarrierSet::enqueue(oop pre_val) {
|
void G1BarrierSet::enqueue(oop pre_val) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -27,7 +27,6 @@
|
||||||
|
|
||||||
#include "gc/g1/g1DirtyCardQueue.hpp"
|
#include "gc/g1/g1DirtyCardQueue.hpp"
|
||||||
#include "gc/g1/g1SATBMarkQueueSet.hpp"
|
#include "gc/g1/g1SATBMarkQueueSet.hpp"
|
||||||
#include "gc/g1/g1SharedDirtyCardQueue.hpp"
|
|
||||||
#include "gc/shared/cardTable.hpp"
|
#include "gc/shared/cardTable.hpp"
|
||||||
#include "gc/shared/cardTableBarrierSet.hpp"
|
#include "gc/shared/cardTableBarrierSet.hpp"
|
||||||
|
|
||||||
|
@ -43,7 +42,6 @@ class G1BarrierSet: public CardTableBarrierSet {
|
||||||
BufferNode::Allocator _dirty_card_queue_buffer_allocator;
|
BufferNode::Allocator _dirty_card_queue_buffer_allocator;
|
||||||
G1SATBMarkQueueSet _satb_mark_queue_set;
|
G1SATBMarkQueueSet _satb_mark_queue_set;
|
||||||
G1DirtyCardQueueSet _dirty_card_queue_set;
|
G1DirtyCardQueueSet _dirty_card_queue_set;
|
||||||
G1SharedDirtyCardQueue _shared_dirty_card_queue;
|
|
||||||
|
|
||||||
static G1BarrierSet* g1_barrier_set() {
|
static G1BarrierSet* g1_barrier_set() {
|
||||||
return barrier_set_cast<G1BarrierSet>(BarrierSet::barrier_set());
|
return barrier_set_cast<G1BarrierSet>(BarrierSet::barrier_set());
|
||||||
|
@ -94,10 +92,6 @@ class G1BarrierSet: public CardTableBarrierSet {
|
||||||
return g1_barrier_set()->_dirty_card_queue_set;
|
return g1_barrier_set()->_dirty_card_queue_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
static G1SharedDirtyCardQueue& shared_dirty_card_queue() {
|
|
||||||
return g1_barrier_set()->_shared_dirty_card_queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callbacks for runtime accesses.
|
// Callbacks for runtime accesses.
|
||||||
template <DecoratorSet decorators, typename BarrierSetT = G1BarrierSet>
|
template <DecoratorSet decorators, typename BarrierSetT = G1BarrierSet>
|
||||||
class AccessBarrier: public ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT> {
|
class AccessBarrier: public ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT> {
|
||||||
|
|
|
@ -546,8 +546,6 @@ void G1DirtyCardQueueSet::abandon_logs() {
|
||||||
}
|
}
|
||||||
} closure(*this);
|
} closure(*this);
|
||||||
Threads::threads_do(&closure);
|
Threads::threads_do(&closure);
|
||||||
|
|
||||||
G1BarrierSet::shared_dirty_card_queue().reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void G1DirtyCardQueueSet::concatenate_logs() {
|
void G1DirtyCardQueueSet::concatenate_logs() {
|
||||||
|
@ -571,7 +569,6 @@ void G1DirtyCardQueueSet::concatenate_logs() {
|
||||||
} closure(*this);
|
} closure(*this);
|
||||||
Threads::threads_do(&closure);
|
Threads::threads_do(&closure);
|
||||||
|
|
||||||
G1BarrierSet::shared_dirty_card_queue().flush();
|
|
||||||
enqueue_all_paused_buffers();
|
enqueue_all_paused_buffers();
|
||||||
verify_num_cards();
|
verify_num_cards();
|
||||||
set_max_cards(old_limit);
|
set_max_cards(old_limit);
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
#include "gc/g1/g1RootClosures.hpp"
|
#include "gc/g1/g1RootClosures.hpp"
|
||||||
#include "gc/g1/g1RemSet.hpp"
|
#include "gc/g1/g1RemSet.hpp"
|
||||||
#include "gc/g1/g1ServiceThread.hpp"
|
#include "gc/g1/g1ServiceThread.hpp"
|
||||||
#include "gc/g1/g1SharedDirtyCardQueue.hpp"
|
|
||||||
#include "gc/g1/g1_globals.hpp"
|
#include "gc/g1/g1_globals.hpp"
|
||||||
#include "gc/g1/heapRegion.inline.hpp"
|
#include "gc/g1/heapRegion.inline.hpp"
|
||||||
#include "gc/g1/heapRegionManager.inline.hpp"
|
#include "gc/g1/heapRegionManager.inline.hpp"
|
||||||
|
@ -1688,12 +1687,27 @@ void G1RemSet::refine_card_concurrently(CardValue* const card_ptr,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-dirty the card and enqueue in the *shared* queue. Can't use
|
enqueue_for_reprocessing(card_ptr);
|
||||||
// the thread-local queue, because that might be the queue that is
|
}
|
||||||
// being processed by us; we could be a Java thread conscripted to
|
|
||||||
// perform refinement on our queue's current buffer.
|
// Re-dirty and re-enqueue the card to retry refinement later.
|
||||||
|
// This is used to deal with a rare race condition in concurrent refinement.
|
||||||
|
void G1RemSet::enqueue_for_reprocessing(CardValue* card_ptr) {
|
||||||
|
// We can't use the thread-local queue, because that might be the queue
|
||||||
|
// that is being processed by us; we could be a Java thread conscripted to
|
||||||
|
// perform refinement on our queue's current buffer. This situation only
|
||||||
|
// arises from rare race condition, so it's not worth any significant
|
||||||
|
// development effort or clever lock-free queue implementation. Instead
|
||||||
|
// we use brute force, allocating and enqueuing an entire buffer for just
|
||||||
|
// this card. Since buffers are processed in FIFO order and we try to
|
||||||
|
// keep some in the queue, it is likely that the racing state will have
|
||||||
|
// resolved by the time this card comes up for reprocessing.
|
||||||
*card_ptr = G1CardTable::dirty_card_val();
|
*card_ptr = G1CardTable::dirty_card_val();
|
||||||
G1BarrierSet::shared_dirty_card_queue().enqueue(card_ptr);
|
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
|
||||||
|
void** buffer = dcqs.allocate_buffer();
|
||||||
|
size_t index = dcqs.buffer_size() - 1;
|
||||||
|
buffer[index] = card_ptr;
|
||||||
|
dcqs.enqueue_completed_buffer(BufferNode::make_node_from_buffer(buffer, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
void G1RemSet::print_periodic_summary_info(const char* header, uint period_count) {
|
void G1RemSet::print_periodic_summary_info(const char* header, uint period_count) {
|
||||||
|
|
|
@ -57,6 +57,9 @@ class HeapRegionClaimer;
|
||||||
// external heap references into it. Uses a mod ref bs to track updates,
|
// external heap references into it. Uses a mod ref bs to track updates,
|
||||||
// so that they can be used to update the individual region remsets.
|
// so that they can be used to update the individual region remsets.
|
||||||
class G1RemSet: public CHeapObj<mtGC> {
|
class G1RemSet: public CHeapObj<mtGC> {
|
||||||
|
public:
|
||||||
|
typedef CardTable::CardValue CardValue;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
G1RemSetScanState* _scan_state;
|
G1RemSetScanState* _scan_state;
|
||||||
|
|
||||||
|
@ -72,10 +75,10 @@ private:
|
||||||
void print_merge_heap_roots_stats();
|
void print_merge_heap_roots_stats();
|
||||||
|
|
||||||
void assert_scan_top_is_null(uint hrm_index) NOT_DEBUG_RETURN;
|
void assert_scan_top_is_null(uint hrm_index) NOT_DEBUG_RETURN;
|
||||||
|
|
||||||
|
void enqueue_for_reprocessing(CardValue* card_ptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
typedef CardTable::CardValue CardValue;
|
|
||||||
|
|
||||||
// Initialize data that depends on the heap size being known.
|
// Initialize data that depends on the heap size being known.
|
||||||
void initialize(uint max_reserved_regions);
|
void initialize(uint max_reserved_regions);
|
||||||
|
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019, 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
|
||||||
#include "gc/g1/g1DirtyCardQueue.hpp"
|
|
||||||
#include "gc/g1/g1SharedDirtyCardQueue.hpp"
|
|
||||||
#include "gc/shared/ptrQueue.hpp"
|
|
||||||
#include "runtime/mutex.hpp"
|
|
||||||
#include "runtime/mutexLocker.hpp"
|
|
||||||
|
|
||||||
G1SharedDirtyCardQueue::G1SharedDirtyCardQueue(G1DirtyCardQueueSet* qset) :
|
|
||||||
_qset(qset),
|
|
||||||
_buffer(NULL),
|
|
||||||
_index(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
G1SharedDirtyCardQueue::~G1SharedDirtyCardQueue() {
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void G1SharedDirtyCardQueue::enqueue(void* card_ptr) {
|
|
||||||
MutexLocker ml(Shared_DirtyCardQ_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
if (_index == 0) {
|
|
||||||
flush();
|
|
||||||
_buffer = _qset->allocate_buffer();
|
|
||||||
_index = _qset->buffer_size();
|
|
||||||
assert(_index != 0, "invariant");
|
|
||||||
}
|
|
||||||
_buffer[--_index] = card_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void G1SharedDirtyCardQueue::flush() {
|
|
||||||
if (_buffer != NULL) {
|
|
||||||
BufferNode* node = BufferNode::make_node_from_buffer(_buffer, _index);
|
|
||||||
_buffer = NULL;
|
|
||||||
_index = 0;
|
|
||||||
if (node->index() == _qset->buffer_size()) {
|
|
||||||
_qset->deallocate_buffer(node);
|
|
||||||
} else {
|
|
||||||
_qset->enqueue_completed_buffer(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(_index == 0, "invariant");
|
|
||||||
}
|
|
||||||
|
|
||||||
void G1SharedDirtyCardQueue::reset() {
|
|
||||||
if (_buffer == NULL) {
|
|
||||||
_index = 0;
|
|
||||||
} else {
|
|
||||||
_index = _qset->buffer_size();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019, 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SHARE_GC_G1_G1SHAREDDIRTYCARDQUEUE_HPP
|
|
||||||
#define SHARE_GC_G1_G1SHAREDDIRTYCARDQUEUE_HPP
|
|
||||||
|
|
||||||
#include "utilities/globalDefinitions.hpp"
|
|
||||||
|
|
||||||
class G1DirtyCardQueueSet;
|
|
||||||
|
|
||||||
// A dirty card queue providing thread-safe enqueue. A shared global
|
|
||||||
// instance can be used for cases where a thread-local dirty card can't
|
|
||||||
// be used.
|
|
||||||
class G1SharedDirtyCardQueue {
|
|
||||||
G1DirtyCardQueueSet* const _qset;
|
|
||||||
void** _buffer;
|
|
||||||
size_t _index;
|
|
||||||
|
|
||||||
NONCOPYABLE(G1SharedDirtyCardQueue);
|
|
||||||
|
|
||||||
public:
|
|
||||||
G1SharedDirtyCardQueue(G1DirtyCardQueueSet* qset);
|
|
||||||
~G1SharedDirtyCardQueue(); // flushes the queue.
|
|
||||||
|
|
||||||
// Thread-safe addition to shared logging buffer.
|
|
||||||
void enqueue(void* card_ptr);
|
|
||||||
|
|
||||||
// Flush any pending entries to the qset and remove the buffer.
|
|
||||||
// Not thread-safe.
|
|
||||||
void flush();
|
|
||||||
|
|
||||||
// Discard any pending entries.
|
|
||||||
// Not thread-safe.
|
|
||||||
void reset();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SHARE_GC_G1_G1SHAREDDIRTYCARDQUEUE_HPP
|
|
|
@ -74,7 +74,6 @@ Mutex* NonJavaThreadsListSync_lock = NULL;
|
||||||
Monitor* CGC_lock = NULL;
|
Monitor* CGC_lock = NULL;
|
||||||
Monitor* STS_lock = NULL;
|
Monitor* STS_lock = NULL;
|
||||||
Monitor* G1OldGCCount_lock = NULL;
|
Monitor* G1OldGCCount_lock = NULL;
|
||||||
Mutex* Shared_DirtyCardQ_lock = NULL;
|
|
||||||
Mutex* G1DetachedRefinementStats_lock = NULL;
|
Mutex* G1DetachedRefinementStats_lock = NULL;
|
||||||
Mutex* MarkStackFreeList_lock = NULL;
|
Mutex* MarkStackFreeList_lock = NULL;
|
||||||
Mutex* MarkStackChunkList_lock = NULL;
|
Mutex* MarkStackChunkList_lock = NULL;
|
||||||
|
@ -217,8 +216,6 @@ void mutex_init() {
|
||||||
if (UseG1GC) {
|
if (UseG1GC) {
|
||||||
def(G1OldGCCount_lock , PaddedMonitor, leaf, true, _safepoint_check_always);
|
def(G1OldGCCount_lock , PaddedMonitor, leaf, true, _safepoint_check_always);
|
||||||
|
|
||||||
def(Shared_DirtyCardQ_lock , PaddedMutex , access + 1, true, _safepoint_check_never);
|
|
||||||
|
|
||||||
def(G1DetachedRefinementStats_lock, PaddedMutex, leaf , true, _safepoint_check_never);
|
def(G1DetachedRefinementStats_lock, PaddedMutex, leaf , true, _safepoint_check_never);
|
||||||
|
|
||||||
def(FreeList_lock , PaddedMutex , leaf , true, _safepoint_check_never);
|
def(FreeList_lock , PaddedMutex , leaf , true, _safepoint_check_never);
|
||||||
|
|
|
@ -68,9 +68,6 @@ extern Monitor* CGC_lock; // used for coordination betwee
|
||||||
// fore- & background GC threads.
|
// fore- & background GC threads.
|
||||||
extern Monitor* STS_lock; // used for joining/leaving SuspendibleThreadSet.
|
extern Monitor* STS_lock; // used for joining/leaving SuspendibleThreadSet.
|
||||||
extern Monitor* G1OldGCCount_lock; // in support of "concurrent" full gc
|
extern Monitor* G1OldGCCount_lock; // in support of "concurrent" full gc
|
||||||
extern Mutex* Shared_DirtyCardQ_lock; // Lock protecting dirty card
|
|
||||||
// queue shared by
|
|
||||||
// non-Java threads.
|
|
||||||
extern Mutex* G1DetachedRefinementStats_lock; // Lock protecting detached refinement stats
|
extern Mutex* G1DetachedRefinementStats_lock; // Lock protecting detached refinement stats
|
||||||
extern Mutex* MarkStackFreeList_lock; // Protects access to the global mark stack free list.
|
extern Mutex* MarkStackFreeList_lock; // Protects access to the global mark stack free list.
|
||||||
extern Mutex* MarkStackChunkList_lock; // Protects access to the global mark stack chunk list.
|
extern Mutex* MarkStackChunkList_lock; // Protects access to the global mark stack chunk list.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue