mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-16 17:14:41 +02:00
7037276: Unnecessary double traversal of dirty card windows
Short-circuited an unnecessary double traversal of dirty card windows when iterating younger refs. Also renamed some cardtable methods for more clarity. Reviewed-by: jmasa, stefank, poonam
This commit is contained in:
parent
12d1d9acc1
commit
b4d40650b2
5 changed files with 198 additions and 162 deletions
|
@ -33,11 +33,11 @@
|
|||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/virtualspace.hpp"
|
||||
|
||||
void CardTableModRefBS::par_non_clean_card_iterate_work(Space* sp, MemRegion mr,
|
||||
void CardTableModRefBS::non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
|
||||
DirtyCardToOopClosure* dcto_cl,
|
||||
MemRegionClosure* cl,
|
||||
ClearNoncleanCardWrapper* cl,
|
||||
int n_threads) {
|
||||
if (n_threads > 0) {
|
||||
assert(n_threads > 0, "Error: expected n_threads > 0");
|
||||
assert((n_threads == 1 && ParallelGCThreads == 0) ||
|
||||
n_threads <= (int)ParallelGCThreads,
|
||||
"# worker threads != # requested!");
|
||||
|
@ -73,7 +73,6 @@ void CardTableModRefBS::par_non_clean_card_iterate_work(Space* sp, MemRegion mr,
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CardTableModRefBS::
|
||||
|
@ -81,7 +80,7 @@ process_stride(Space* sp,
|
|||
MemRegion used,
|
||||
jint stride, int n_strides,
|
||||
DirtyCardToOopClosure* dcto_cl,
|
||||
MemRegionClosure* cl,
|
||||
ClearNoncleanCardWrapper* cl,
|
||||
jbyte** lowest_non_clean,
|
||||
uintptr_t lowest_non_clean_base_chunk_index,
|
||||
size_t lowest_non_clean_chunk_size) {
|
||||
|
@ -127,7 +126,11 @@ process_stride(Space* sp,
|
|||
lowest_non_clean_base_chunk_index,
|
||||
lowest_non_clean_chunk_size);
|
||||
|
||||
non_clean_card_iterate_work(chunk_mr, cl);
|
||||
// We do not call the non_clean_card_iterate_serial() version because
|
||||
// we want to clear the cards, and the ClearNoncleanCardWrapper closure
|
||||
// itself does the work of finding contiguous dirty ranges of cards to
|
||||
// process (and clear).
|
||||
cl->do_MemRegion(chunk_mr);
|
||||
|
||||
// Find the next chunk of the stride.
|
||||
chunk_card_start += CardsPerStrideChunk * n_strides;
|
||||
|
|
|
@ -456,30 +456,34 @@ bool CardTableModRefBS::mark_card_deferred(size_t card_index) {
|
|||
}
|
||||
|
||||
|
||||
void CardTableModRefBS::non_clean_card_iterate(Space* sp,
|
||||
void CardTableModRefBS::non_clean_card_iterate_possibly_parallel(Space* sp,
|
||||
MemRegion mr,
|
||||
DirtyCardToOopClosure* dcto_cl,
|
||||
MemRegionClosure* cl) {
|
||||
ClearNoncleanCardWrapper* cl) {
|
||||
if (!mr.is_empty()) {
|
||||
int n_threads = SharedHeap::heap()->n_par_threads();
|
||||
if (n_threads > 0) {
|
||||
#ifndef SERIALGC
|
||||
par_non_clean_card_iterate_work(sp, mr, dcto_cl, cl, n_threads);
|
||||
non_clean_card_iterate_parallel_work(sp, mr, dcto_cl, cl, n_threads);
|
||||
#else // SERIALGC
|
||||
fatal("Parallel gc not supported here.");
|
||||
#endif // SERIALGC
|
||||
} else {
|
||||
non_clean_card_iterate_work(mr, cl);
|
||||
// We do not call the non_clean_card_iterate_serial() version below because
|
||||
// we want to clear the cards (which non_clean_card_iterate_serial() does not
|
||||
// do for us), and the ClearNoncleanCardWrapper closure itself does the work
|
||||
// of finding contiguous dirty ranges of cards to process (and clear).
|
||||
cl->do_MemRegion(mr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: For this to work correctly, it is important that
|
||||
// we look for non-clean cards below (so as to catch those
|
||||
// marked precleaned), rather than look explicitly for dirty
|
||||
// cards (and miss those marked precleaned). In that sense,
|
||||
// the name precleaned is currently somewhat of a misnomer.
|
||||
void CardTableModRefBS::non_clean_card_iterate_work(MemRegion mr,
|
||||
// The iterator itself is not MT-aware, but
|
||||
// MT-aware callers and closures can use this to
|
||||
// accomplish dirty card iteration in parallel. The
|
||||
// iterator itself does not clear the dirty cards, or
|
||||
// change their values in any manner.
|
||||
void CardTableModRefBS::non_clean_card_iterate_serial(MemRegion mr,
|
||||
MemRegionClosure* cl) {
|
||||
for (int i = 0; i < _cur_covered_regions; i++) {
|
||||
MemRegion mri = mr.intersection(_covered[i]);
|
||||
|
@ -661,7 +665,7 @@ public:
|
|||
|
||||
void CardTableModRefBS::verify_clean_region(MemRegion mr) {
|
||||
GuaranteeNotModClosure blk(this);
|
||||
non_clean_card_iterate_work(mr, &blk);
|
||||
non_clean_card_iterate_serial(mr, &blk);
|
||||
}
|
||||
|
||||
// To verify a MemRegion is entirely dirty this closure is passed to
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
class Generation;
|
||||
class OopsInGenClosure;
|
||||
class DirtyCardToOopClosure;
|
||||
class ClearNoncleanCardWrapper;
|
||||
|
||||
class CardTableModRefBS: public ModRefBarrierSet {
|
||||
// Some classes get to look at some private stuff.
|
||||
|
@ -165,22 +166,28 @@ class CardTableModRefBS: public ModRefBarrierSet {
|
|||
|
||||
// Iterate over the portion of the card-table which covers the given
|
||||
// region mr in the given space and apply cl to any dirty sub-regions
|
||||
// of mr. cl and dcto_cl must either be the same closure or cl must
|
||||
// wrap dcto_cl. Both are required - neither may be NULL. Also, dcto_cl
|
||||
// may be modified. Note that this function will operate in a parallel
|
||||
// mode if worker threads are available.
|
||||
void non_clean_card_iterate(Space* sp, MemRegion mr,
|
||||
DirtyCardToOopClosure* dcto_cl,
|
||||
MemRegionClosure* cl);
|
||||
// of mr. Dirty cards are _not_ cleared by the iterator method itself,
|
||||
// but closures may arrange to do so on their own should they so wish.
|
||||
void non_clean_card_iterate_serial(MemRegion mr, MemRegionClosure* cl);
|
||||
|
||||
// Utility function used to implement the other versions below.
|
||||
void non_clean_card_iterate_work(MemRegion mr, MemRegionClosure* cl);
|
||||
|
||||
void par_non_clean_card_iterate_work(Space* sp, MemRegion mr,
|
||||
// A variant of the above that will operate in a parallel mode if
|
||||
// worker threads are available, and clear the dirty cards as it
|
||||
// processes them.
|
||||
// ClearNoncleanCardWrapper cl must wrap the DirtyCardToOopClosure dcto_cl,
|
||||
// which may itself be modified by the method.
|
||||
void non_clean_card_iterate_possibly_parallel(Space* sp, MemRegion mr,
|
||||
DirtyCardToOopClosure* dcto_cl,
|
||||
MemRegionClosure* cl,
|
||||
ClearNoncleanCardWrapper* cl);
|
||||
|
||||
private:
|
||||
// Work method used to implement non_clean_card_iterate_possibly_parallel()
|
||||
// above in the parallel case.
|
||||
void non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
|
||||
DirtyCardToOopClosure* dcto_cl,
|
||||
ClearNoncleanCardWrapper* cl,
|
||||
int n_threads);
|
||||
|
||||
protected:
|
||||
// Dirty the bytes corresponding to "mr" (not all of which must be
|
||||
// covered.)
|
||||
void dirty_MemRegion(MemRegion mr);
|
||||
|
@ -237,7 +244,7 @@ class CardTableModRefBS: public ModRefBarrierSet {
|
|||
MemRegion used,
|
||||
jint stride, int n_strides,
|
||||
DirtyCardToOopClosure* dcto_cl,
|
||||
MemRegionClosure* cl,
|
||||
ClearNoncleanCardWrapper* cl,
|
||||
jbyte** lowest_non_clean,
|
||||
uintptr_t lowest_non_clean_base_chunk_index,
|
||||
size_t lowest_non_clean_chunk_size);
|
||||
|
@ -409,14 +416,14 @@ public:
|
|||
// marking, where a dirty card may cause scanning, and summarization
|
||||
// marking, of objects that extend onto subsequent cards.)
|
||||
void mod_card_iterate(MemRegionClosure* cl) {
|
||||
non_clean_card_iterate_work(_whole_heap, cl);
|
||||
non_clean_card_iterate_serial(_whole_heap, cl);
|
||||
}
|
||||
|
||||
// Like the "mod_cards_iterate" above, except only invokes the closure
|
||||
// for cards within the MemRegion "mr" (which is required to be
|
||||
// card-aligned and sized.)
|
||||
void mod_card_iterate(MemRegion mr, MemRegionClosure* cl) {
|
||||
non_clean_card_iterate_work(mr, cl);
|
||||
non_clean_card_iterate_serial(mr, cl);
|
||||
}
|
||||
|
||||
static uintx ct_max_alignment_constraint();
|
||||
|
@ -493,4 +500,5 @@ public:
|
|||
void set_CTRS(CardTableRS* rs) { _rs = rs; }
|
||||
};
|
||||
|
||||
|
||||
#endif // SHARE_VM_MEMORY_CARDTABLEMODREFBS_HPP
|
||||
|
|
|
@ -105,15 +105,15 @@ void CardTableRS::younger_refs_iterate(Generation* g,
|
|||
g->younger_refs_iterate(blk);
|
||||
}
|
||||
|
||||
class ClearNoncleanCardWrapper: public MemRegionClosure {
|
||||
MemRegionClosure* _dirty_card_closure;
|
||||
CardTableRS* _ct;
|
||||
bool _is_par;
|
||||
private:
|
||||
// Clears the given card, return true if the corresponding card should be
|
||||
// processed.
|
||||
bool clear_card(jbyte* entry) {
|
||||
inline bool ClearNoncleanCardWrapper::clear_card(jbyte* entry) {
|
||||
if (_is_par) {
|
||||
return clear_card_parallel(entry);
|
||||
} else {
|
||||
return clear_card_serial(entry);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ClearNoncleanCardWrapper::clear_card_parallel(jbyte* entry) {
|
||||
while (true) {
|
||||
// In the parallel case, we may have to do this several times.
|
||||
jbyte entry_val = *entry;
|
||||
|
@ -147,7 +147,10 @@ private:
|
|||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
|
||||
|
||||
inline bool ClearNoncleanCardWrapper::clear_card_serial(jbyte* entry) {
|
||||
jbyte entry_val = *entry;
|
||||
assert(entry_val != CardTableRS::clean_card_val(),
|
||||
"We shouldn't be looking at clean cards, and this should "
|
||||
|
@ -157,55 +160,56 @@ private:
|
|||
*entry = CardTableRS::clean_card_val();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
ClearNoncleanCardWrapper(MemRegionClosure* dirty_card_closure,
|
||||
CardTableRS* ct) :
|
||||
ClearNoncleanCardWrapper::ClearNoncleanCardWrapper(
|
||||
MemRegionClosure* dirty_card_closure, CardTableRS* ct) :
|
||||
_dirty_card_closure(dirty_card_closure), _ct(ct) {
|
||||
_is_par = (SharedHeap::heap()->n_par_threads() > 0);
|
||||
}
|
||||
void do_MemRegion(MemRegion mr) {
|
||||
// We start at the high end of "mr", walking backwards
|
||||
// while accumulating a contiguous dirty range of cards in
|
||||
// [start_of_non_clean, end_of_non_clean) which we then
|
||||
// process en masse.
|
||||
|
||||
void ClearNoncleanCardWrapper::do_MemRegion(MemRegion mr) {
|
||||
assert(mr.word_size() > 0, "Error");
|
||||
assert(_ct->is_aligned(mr.start()), "mr.start() should be card aligned");
|
||||
// mr.end() may not necessarily be card aligned.
|
||||
jbyte* cur_entry = _ct->byte_for(mr.last());
|
||||
const jbyte* limit = _ct->byte_for(mr.start());
|
||||
HeapWord* end_of_non_clean = mr.end();
|
||||
HeapWord* start_of_non_clean = end_of_non_clean;
|
||||
jbyte* entry = _ct->byte_for(mr.last());
|
||||
const jbyte* first_entry = _ct->byte_for(mr.start());
|
||||
while (entry >= first_entry) {
|
||||
HeapWord* cur = _ct->addr_for(entry);
|
||||
if (!clear_card(entry)) {
|
||||
// We hit a clean card; process any non-empty
|
||||
// dirty range accumulated so far.
|
||||
while (cur_entry >= limit) {
|
||||
HeapWord* cur_hw = _ct->addr_for(cur_entry);
|
||||
if ((*cur_entry != CardTableRS::clean_card_val()) && clear_card(cur_entry)) {
|
||||
// Continue the dirty range by opening the
|
||||
// dirty window one card to the left.
|
||||
start_of_non_clean = cur_hw;
|
||||
} else {
|
||||
// We hit a "clean" card; process any non-empty
|
||||
// "dirty" range accumulated so far.
|
||||
if (start_of_non_clean < end_of_non_clean) {
|
||||
MemRegion mr2(start_of_non_clean, end_of_non_clean);
|
||||
_dirty_card_closure->do_MemRegion(mr2);
|
||||
const MemRegion mrd(start_of_non_clean, end_of_non_clean);
|
||||
_dirty_card_closure->do_MemRegion(mrd);
|
||||
}
|
||||
// Reset the dirty window while continuing to
|
||||
// look for the next dirty window to process.
|
||||
end_of_non_clean = cur;
|
||||
start_of_non_clean = end_of_non_clean;
|
||||
// Reset the dirty window, while continuing to look
|
||||
// for the next dirty card that will start a
|
||||
// new dirty window.
|
||||
end_of_non_clean = cur_hw;
|
||||
start_of_non_clean = cur_hw;
|
||||
}
|
||||
// Open the left end of the window one card to the left.
|
||||
start_of_non_clean = cur;
|
||||
// Note that "entry" leads "start_of_non_clean" in
|
||||
// Note that "cur_entry" leads "start_of_non_clean" in
|
||||
// its leftward excursion after this point
|
||||
// in the loop and, when we hit the left end of "mr",
|
||||
// will point off of the left end of the card-table
|
||||
// for "mr".
|
||||
entry--;
|
||||
cur_entry--;
|
||||
}
|
||||
// If the first card of "mr" was dirty, we will have
|
||||
// been left with a dirty window, co-initial with "mr",
|
||||
// which we now process.
|
||||
if (start_of_non_clean < end_of_non_clean) {
|
||||
MemRegion mr2(start_of_non_clean, end_of_non_clean);
|
||||
_dirty_card_closure->do_MemRegion(mr2);
|
||||
const MemRegion mrd(start_of_non_clean, end_of_non_clean);
|
||||
_dirty_card_closure->do_MemRegion(mrd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// clean (by dirty->clean before) ==> cur_younger_gen
|
||||
// dirty ==> cur_youngergen_and_prev_nonclean_card
|
||||
// precleaned ==> cur_youngergen_and_prev_nonclean_card
|
||||
|
@ -246,7 +250,7 @@ void CardTableRS::younger_refs_in_space_iterate(Space* sp,
|
|||
cl->gen_boundary());
|
||||
ClearNoncleanCardWrapper clear_cl(dcto_cl, this);
|
||||
|
||||
_ct_bs->non_clean_card_iterate(sp, sp->used_region_at_save_marks(),
|
||||
_ct_bs->non_clean_card_iterate_possibly_parallel(sp, sp->used_region_at_save_marks(),
|
||||
dcto_cl, &clear_cl);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2011, 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
|
||||
|
@ -166,4 +166,21 @@ public:
|
|||
|
||||
};
|
||||
|
||||
class ClearNoncleanCardWrapper: public MemRegionClosure {
|
||||
MemRegionClosure* _dirty_card_closure;
|
||||
CardTableRS* _ct;
|
||||
bool _is_par;
|
||||
private:
|
||||
// Clears the given card, return true if the corresponding card should be
|
||||
// processed.
|
||||
inline bool clear_card(jbyte* entry);
|
||||
// Work methods called by the clear_card()
|
||||
inline bool clear_card_serial(jbyte* entry);
|
||||
inline bool clear_card_parallel(jbyte* entry);
|
||||
|
||||
public:
|
||||
ClearNoncleanCardWrapper(MemRegionClosure* dirty_card_closure, CardTableRS* ct);
|
||||
void do_MemRegion(MemRegion mr);
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_MEMORY_CARDTABLERS_HPP
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue