Skip to content

Commit 40565a8

Browse files
author
Kim Barrett
committed
8230404: Refactor logged card refinement support in G1DirtyCardQueueSet
Separate concurrent refinement from STW refinement. Reviewed-by: sjohanss, tschatzl
1 parent c159a4e commit 40565a8

File tree

7 files changed

+89
-131
lines changed

7 files changed

+89
-131
lines changed

src/hotspot/share/gc/g1/g1CardTableEntryClosure.hpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#define SHARE_GC_G1_G1CARDTABLEENTRYCLOSURE_HPP
2727

2828
#include "gc/shared/cardTable.hpp"
29+
#include "gc/shared/ptrQueue.hpp"
2930
#include "memory/allocation.hpp"
3031

3132
// A closure class for processing card table entries. Note that we don't
@@ -34,9 +35,17 @@ class G1CardTableEntryClosure: public CHeapObj<mtGC> {
3435
public:
3536
typedef CardTable::CardValue CardValue;
3637

37-
// Process the card whose card table entry is "card_ptr". If returns
38-
// "false", terminate the iteration early.
39-
virtual bool do_card_ptr(CardValue* card_ptr, uint worker_id) = 0;
38+
// Process the card whose card table entry is "card_ptr".
39+
virtual void do_card_ptr(CardValue* card_ptr, uint worker_id) = 0;
40+
41+
// Process all the card_ptrs in node.
42+
void apply_to_buffer(BufferNode* node, size_t buffer_size, uint worker_id) {
43+
void** buffer = BufferNode::make_buffer_from_node(node);
44+
for (size_t i = node->index(); i < buffer_size; ++i) {
45+
CardValue* card_ptr = static_cast<CardValue*>(buffer[i]);
46+
do_card_ptr(card_ptr, worker_id);
47+
}
48+
}
4049
};
4150

4251
#endif // SHARE_GC_G1_G1CARDTABLEENTRYCLOSURE_HPP

src/hotspot/share/gc/g1/g1CollectedHeap.cpp

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,14 @@ class RedirtyLoggedCardTableEntryClosure : public G1CardTableEntryClosure {
132132
RedirtyLoggedCardTableEntryClosure(G1CollectedHeap* g1h) : G1CardTableEntryClosure(),
133133
_num_dirtied(0), _g1h(g1h), _g1_ct(g1h->card_table()) { }
134134

135-
bool do_card_ptr(CardValue* card_ptr, uint worker_i) {
135+
void do_card_ptr(CardValue* card_ptr, uint worker_i) {
136136
HeapRegion* hr = region_for_card(card_ptr);
137137

138138
// Should only dirty cards in regions that won't be freed.
139139
if (!will_become_free(hr)) {
140140
*card_ptr = G1CardTable::dirty_card_val();
141141
_num_dirtied++;
142142
}
143-
144-
return true;
145143
}
146144

147145
size_t num_dirtied() const { return _num_dirtied; }
@@ -1948,12 +1946,6 @@ void G1CollectedHeap::iterate_hcc_closure(G1CardTableEntryClosure* cl, uint work
19481946
_hot_card_cache->drain(cl, worker_i);
19491947
}
19501948

1951-
void G1CollectedHeap::iterate_dirty_card_closure(G1CardTableEntryClosure* cl, uint worker_i) {
1952-
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
1953-
while (dcqs.apply_closure_during_gc(cl, worker_i)) {}
1954-
assert(dcqs.num_cards() == 0, "Completed buffers exist!");
1955-
}
1956-
19571949
// Computes the sum of the storage used by the various regions.
19581950
size_t G1CollectedHeap::used() const {
19591951
size_t result = _summary_bytes_used + _allocator->used_in_alloc_regions();
@@ -3225,23 +3217,14 @@ class G1RedirtyLoggedCardsTask : public AbstractGangTask {
32253217
G1CollectedHeap* _g1h;
32263218
BufferNode* volatile _nodes;
32273219

3228-
void apply(G1CardTableEntryClosure* cl, BufferNode* node, uint worker_id) {
3229-
void** buf = BufferNode::make_buffer_from_node(node);
3230-
size_t limit = _qset->buffer_size();
3231-
for (size_t i = node->index(); i < limit; ++i) {
3232-
CardTable::CardValue* card_ptr = static_cast<CardTable::CardValue*>(buf[i]);
3233-
bool result = cl->do_card_ptr(card_ptr, worker_id);
3234-
assert(result, "Closure should always return true");
3235-
}
3236-
}
3237-
3238-
void par_apply(G1CardTableEntryClosure* cl, uint worker_id) {
3220+
void par_apply(RedirtyLoggedCardTableEntryClosure* cl, uint worker_id) {
3221+
size_t buffer_size = _qset->buffer_size();
32393222
BufferNode* next = Atomic::load(&_nodes);
32403223
while (next != NULL) {
32413224
BufferNode* node = next;
32423225
next = Atomic::cmpxchg(node->next(), &_nodes, node);
32433226
if (next == node) {
3244-
apply(cl, node, worker_id);
3227+
cl->apply_to_buffer(node, buffer_size, worker_id);
32453228
next = node->next();
32463229
}
32473230
}

src/hotspot/share/gc/g1/g1CollectedHeap.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -993,9 +993,6 @@ class G1CollectedHeap : public CollectedHeap {
993993
// Apply the given closure on all cards in the Hot Card Cache, emptying it.
994994
void iterate_hcc_closure(G1CardTableEntryClosure* cl, uint worker_i);
995995

996-
// Apply the given closure on all cards in the Dirty Card Queue Set, emptying it.
997-
void iterate_dirty_card_closure(G1CardTableEntryClosure* cl, uint worker_i);
998-
999996
// The shared block offset table array.
1000997
G1BlockOffsetTable* bot() const { return _bot; }
1001998

src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp

Lines changed: 30 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,6 @@
4141
#include "runtime/thread.inline.hpp"
4242
#include "runtime/threadSMR.hpp"
4343

44-
// Closure used for updating remembered sets and recording references that
45-
// point into the collection set while the mutator is running.
46-
// Assumed to be only executed concurrently with the mutator. Yields via
47-
// SuspendibleThreadSet after every card.
48-
class G1RefineCardConcurrentlyClosure: public G1CardTableEntryClosure {
49-
public:
50-
bool do_card_ptr(CardValue* card_ptr, uint worker_i) {
51-
G1CollectedHeap::heap()->rem_set()->refine_card_concurrently(card_ptr, worker_i);
52-
53-
if (SuspendibleThreadSet::should_yield()) {
54-
// Caller will actually yield.
55-
return false;
56-
}
57-
// Otherwise, we finished successfully; return true.
58-
return true;
59-
}
60-
};
61-
6244
G1DirtyCardQueue::G1DirtyCardQueue(G1DirtyCardQueueSet* qset) :
6345
// Dirty card queues are always active, so we create them with their
6446
// active field set to true.
@@ -228,25 +210,27 @@ void G1DirtyCardQueueSet::merge_bufferlists(G1RedirtyCardsQueueSet* src) {
228210
verify_num_cards();
229211
}
230212

231-
bool G1DirtyCardQueueSet::apply_closure_to_buffer(G1CardTableEntryClosure* cl,
232-
BufferNode* node,
233-
uint worker_i) {
234-
if (cl == NULL) return true;
235-
bool result = true;
236-
void** buf = BufferNode::make_buffer_from_node(node);
213+
G1BufferNodeList G1DirtyCardQueueSet::take_all_completed_buffers() {
214+
MutexLocker x(_cbl_mon, Mutex::_no_safepoint_check_flag);
215+
G1BufferNodeList result(_completed_buffers_head, _completed_buffers_tail, _num_cards);
216+
_completed_buffers_head = NULL;
217+
_completed_buffers_tail = NULL;
218+
_num_cards = 0;
219+
return result;
220+
}
221+
222+
bool G1DirtyCardQueueSet::refine_buffer(BufferNode* node, uint worker_id) {
223+
G1RemSet* rem_set = G1CollectedHeap::heap()->rem_set();
224+
size_t size = buffer_size();
225+
void** buffer = BufferNode::make_buffer_from_node(node);
237226
size_t i = node->index();
238-
size_t limit = buffer_size();
239-
for ( ; i < limit; ++i) {
240-
CardTable::CardValue* card_ptr = static_cast<CardTable::CardValue*>(buf[i]);
241-
assert(card_ptr != NULL, "invariant");
242-
if (!cl->do_card_ptr(card_ptr, worker_i)) {
243-
result = false; // Incomplete processing.
244-
break;
245-
}
227+
assert(i <= size, "invariant");
228+
for ( ; (i < size) && !SuspendibleThreadSet::should_yield(); ++i) {
229+
CardTable::CardValue* cp = static_cast<CardTable::CardValue*>(buffer[i]);
230+
rem_set->refine_card_concurrently(cp, worker_id);
246231
}
247-
assert(i <= buffer_size(), "invariant");
248232
node->set_index(i);
249-
return result;
233+
return i == size;
250234
}
251235

252236
#ifndef ASSERT
@@ -282,8 +266,7 @@ bool G1DirtyCardQueueSet::process_or_enqueue_completed_buffer(BufferNode* node)
282266

283267
bool G1DirtyCardQueueSet::mut_process_buffer(BufferNode* node) {
284268
uint worker_id = _free_ids.claim_par_id(); // temporarily claim an id
285-
G1RefineCardConcurrentlyClosure cl;
286-
bool result = apply_closure_to_buffer(&cl, node, worker_id);
269+
bool result = refine_buffer(node, worker_id);
287270
_free_ids.release_par_id(worker_id); // release the id
288271

289272
if (result) {
@@ -293,35 +276,19 @@ bool G1DirtyCardQueueSet::mut_process_buffer(BufferNode* node) {
293276
return result;
294277
}
295278

296-
bool G1DirtyCardQueueSet::refine_completed_buffer_concurrently(uint worker_i, size_t stop_at) {
297-
G1RefineCardConcurrentlyClosure cl;
298-
return apply_closure_to_completed_buffer(&cl, worker_i, stop_at, false);
299-
}
300-
301-
bool G1DirtyCardQueueSet::apply_closure_during_gc(G1CardTableEntryClosure* cl, uint worker_i) {
302-
assert_at_safepoint();
303-
return apply_closure_to_completed_buffer(cl, worker_i, 0, true);
304-
}
305-
306-
bool G1DirtyCardQueueSet::apply_closure_to_completed_buffer(G1CardTableEntryClosure* cl,
307-
uint worker_i,
308-
size_t stop_at,
309-
bool during_pause) {
310-
assert(!during_pause || stop_at == 0, "Should not leave any completed buffers during a pause");
311-
BufferNode* nd = get_completed_buffer(stop_at);
312-
if (nd == NULL) {
279+
bool G1DirtyCardQueueSet::refine_completed_buffer_concurrently(uint worker_id, size_t stop_at) {
280+
BufferNode* node = get_completed_buffer(stop_at);
281+
if (node == NULL) {
313282
return false;
283+
} else if (refine_buffer(node, worker_id)) {
284+
assert_fully_consumed(node, buffer_size());
285+
// Done with fully processed buffer.
286+
deallocate_buffer(node);
287+
Atomic::inc(&_processed_buffers_rs_thread);
288+
return true;
314289
} else {
315-
if (apply_closure_to_buffer(cl, nd, worker_i)) {
316-
assert_fully_consumed(nd, buffer_size());
317-
// Done with fully processed buffer.
318-
deallocate_buffer(nd);
319-
Atomic::inc(&_processed_buffers_rs_thread);
320-
} else {
321-
// Return partially processed buffer to the queue.
322-
guarantee(!during_pause, "Should never stop early");
323-
enqueue_completed_buffer(nd);
324-
}
290+
// Return partially processed buffer to the queue.
291+
enqueue_completed_buffer(node);
325292
return true;
326293
}
327294
}

src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
#ifndef SHARE_GC_G1_G1DIRTYCARDQUEUE_HPP
2626
#define SHARE_GC_G1_G1DIRTYCARDQUEUE_HPP
2727

28+
#include "gc/g1/g1BufferNodeList.hpp"
2829
#include "gc/g1/g1FreeIdSet.hpp"
2930
#include "gc/shared/ptrQueue.hpp"
3031
#include "memory/allocation.hpp"
3132

32-
class G1CardTableEntryClosure;
3333
class G1DirtyCardQueueSet;
3434
class G1RedirtyCardsQueueSet;
3535
class Thread;
@@ -78,34 +78,14 @@ class G1DirtyCardQueueSet: public PtrQueueSet {
7878

7979
void abandon_completed_buffers();
8080

81-
// Apply the closure to the elements of "node" from it's index to
82-
// buffer_size. If all closure applications return true, then
83-
// returns true. Stops processing after the first closure
84-
// application that returns false, and returns false from this
85-
// function. The node's index is updated to exclude the processed
86-
// elements, e.g. up to the element for which the closure returned
87-
// false, or one past the last element if the closure always
88-
// returned true.
89-
bool apply_closure_to_buffer(G1CardTableEntryClosure* cl,
90-
BufferNode* node,
91-
uint worker_i = 0);
92-
93-
// If there are more than stop_at completed buffers, pop one, apply
94-
// the specified closure to its active elements, and return true.
95-
// Otherwise return false.
96-
//
97-
// A completely processed buffer is freed. However, if a closure
98-
// invocation returns false, processing is stopped and the partially
99-
// processed buffer (with its index updated to exclude the processed
100-
// elements, e.g. up to the element for which the closure returned
101-
// false) is returned to the completed buffer set.
102-
//
103-
// If during_pause is true, stop_at must be zero, and the closure
104-
// must never return false.
105-
bool apply_closure_to_completed_buffer(G1CardTableEntryClosure* cl,
106-
uint worker_i,
107-
size_t stop_at,
108-
bool during_pause);
81+
// Refine the cards in "node" from it's index to buffer_size.
82+
// Stops processing if SuspendibleThreadSet::should_yield() is true.
83+
// Returns true if the entire buffer was processed, false if there
84+
// is a pending yield request. The node's index is updated to exclude
85+
// the processed elements, e.g. up to the element before processing
86+
// stopped, or one past the last element if the entire buffer was
87+
// processed.
88+
bool refine_buffer(BufferNode* node, uint worker_id);
10989

11090
bool mut_process_buffer(BufferNode* node);
11191

@@ -171,13 +151,16 @@ class G1DirtyCardQueueSet: public PtrQueueSet {
171151

172152
void merge_bufferlists(G1RedirtyCardsQueueSet* src);
173153

174-
// Apply G1RefineCardConcurrentlyClosure to completed buffers until there are stop_at
175-
// completed buffers remaining.
176-
bool refine_completed_buffer_concurrently(uint worker_i, size_t stop_at);
154+
G1BufferNodeList take_all_completed_buffers();
177155

178-
// Apply the given closure to all completed buffers. The given closure's do_card_ptr
179-
// must never return false. Must only be called during GC.
180-
bool apply_closure_during_gc(G1CardTableEntryClosure* cl, uint worker_i);
156+
// If there are more than stop_at cards in the completed buffers, pop
157+
// a buffer, refine its contents, and return true. Otherwise return
158+
// false.
159+
//
160+
// Stops processing a buffer if SuspendibleThreadSet::should_yield(),
161+
// returning the incompletely processed buffer to the completed buffer
162+
// list, for later processing of the remainder.
163+
bool refine_completed_buffer_concurrently(uint worker_i, size_t stop_at);
181164

182165
// If a full collection is happening, reset partial logs, and release
183166
// completed ones: the full collection will make them all irrelevant.

src/hotspot/share/gc/g1/g1HotCardCache.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,7 @@ void G1HotCardCache::drain(G1CardTableEntryClosure* cl, uint worker_i) {
9999
for (size_t i = start_idx; i < end_idx; i++) {
100100
CardValue* card_ptr = _hot_cache[i];
101101
if (card_ptr != NULL) {
102-
bool result = cl->do_card_ptr(card_ptr, worker_i);
103-
assert(result, "Closure should always return true");
102+
cl->do_card_ptr(card_ptr, worker_i);
104103
} else {
105104
break;
106105
}

src/hotspot/share/gc/g1/g1RemSet.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "gc/g1/heapRegionRemSet.inline.hpp"
4343
#include "gc/g1/sparsePRT.hpp"
4444
#include "gc/shared/gcTraceTime.inline.hpp"
45+
#include "gc/shared/ptrQueue.hpp"
4546
#include "gc/shared/suspendibleThreadSet.hpp"
4647
#include "jfr/jfrEvents.hpp"
4748
#include "memory/iterator.hpp"
@@ -1060,7 +1061,7 @@ class G1MergeHeapRootsTask : public AbstractGangTask {
10601061
_scan_state(scan_state), _ct(g1h->card_table()), _cards_dirty(0), _cards_skipped(0)
10611062
{}
10621063

1063-
bool do_card_ptr(CardValue* card_ptr, uint worker_i) {
1064+
void do_card_ptr(CardValue* card_ptr, uint worker_i) {
10641065
// The only time we care about recording cards that
10651066
// contain references that point into the collection set
10661067
// is during RSet updating within an evacuation pause.
@@ -1084,7 +1085,6 @@ class G1MergeHeapRootsTask : public AbstractGangTask {
10841085
// regions to clear the card table at the end during the prepare() phase.
10851086
_cards_skipped++;
10861087
}
1087-
return true;
10881088
}
10891089

10901090
size_t cards_dirty() const { return _cards_dirty; }
@@ -1093,17 +1093,37 @@ class G1MergeHeapRootsTask : public AbstractGangTask {
10931093

10941094
HeapRegionClaimer _hr_claimer;
10951095
G1RemSetScanState* _scan_state;
1096+
BufferNode::Stack _dirty_card_buffers;
10961097
bool _initial_evacuation;
10971098

10981099
volatile bool _fast_reclaim_handled;
10991100

1101+
void apply_closure_to_dirty_card_buffers(G1MergeLogBufferCardsClosure* cl, uint worker_id) {
1102+
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
1103+
size_t buffer_size = dcqs.buffer_size();
1104+
while (BufferNode* node = _dirty_card_buffers.pop()) {
1105+
cl->apply_to_buffer(node, buffer_size, worker_id);
1106+
dcqs.deallocate_buffer(node);
1107+
}
1108+
}
1109+
11001110
public:
11011111
G1MergeHeapRootsTask(G1RemSetScanState* scan_state, uint num_workers, bool initial_evacuation) :
11021112
AbstractGangTask("G1 Merge Heap Roots"),
11031113
_hr_claimer(num_workers),
11041114
_scan_state(scan_state),
1115+
_dirty_card_buffers(),
11051116
_initial_evacuation(initial_evacuation),
1106-
_fast_reclaim_handled(false) { }
1117+
_fast_reclaim_handled(false)
1118+
{
1119+
if (initial_evacuation) {
1120+
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
1121+
G1BufferNodeList buffers = dcqs.take_all_completed_buffers();
1122+
if (buffers._entry_count != 0) {
1123+
_dirty_card_buffers.prepend(*buffers._head, *buffers._tail);
1124+
}
1125+
}
1126+
}
11071127

11081128
virtual void work(uint worker_id) {
11091129
G1CollectedHeap* g1h = G1CollectedHeap::heap();
@@ -1158,7 +1178,7 @@ class G1MergeHeapRootsTask : public AbstractGangTask {
11581178
G1GCParPhaseTimesTracker x(p, G1GCPhaseTimes::MergeLB, worker_id);
11591179

11601180
G1MergeLogBufferCardsClosure cl(g1h, _scan_state);
1161-
g1h->iterate_dirty_card_closure(&cl, worker_id);
1181+
apply_closure_to_dirty_card_buffers(&cl, worker_id);
11621182

11631183
p->record_thread_work_item(G1GCPhaseTimes::MergeLB, worker_id, cl.cards_dirty(), G1GCPhaseTimes::MergeLBDirtyCards);
11641184
p->record_thread_work_item(G1GCPhaseTimes::MergeLB, worker_id, cl.cards_skipped(), G1GCPhaseTimes::MergeLBSkippedCards);

0 commit comments

Comments
 (0)