mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
7034139: G1: assert(Thread::current()->is_ConcurrentGC_thread()) failed: only a conc GC thread can call this
We were calling STS join and leave during a STW pause and we are not suppoesed to. I now only call those during concurrent phase. I also added stress code in the non-product builds to force an overflows (the condition that ws uncovering the bug) to make sure it does not happen again. Reviewed-by: johnc, brutisso
This commit is contained in:
parent
5b3550c107
commit
8c04c76193
3 changed files with 125 additions and 22 deletions
|
@ -826,6 +826,14 @@ public:
|
||||||
void ConcurrentMark::checkpointRootsInitialPost() {
|
void ConcurrentMark::checkpointRootsInitialPost() {
|
||||||
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||||
|
|
||||||
|
// If we force an overflow during remark, the remark operation will
|
||||||
|
// actually abort and we'll restart concurrent marking. If we always
|
||||||
|
// force an oveflow during remark we'll never actually complete the
|
||||||
|
// marking phase. So, we initilize this here, at the start of the
|
||||||
|
// cycle, so that at the remaining overflow number will decrease at
|
||||||
|
// every remark and we'll eventually not need to cause one.
|
||||||
|
force_overflow_stw()->init();
|
||||||
|
|
||||||
// For each region note start of marking.
|
// For each region note start of marking.
|
||||||
NoteStartOfMarkHRClosure startcl;
|
NoteStartOfMarkHRClosure startcl;
|
||||||
g1h->heap_region_iterate(&startcl);
|
g1h->heap_region_iterate(&startcl);
|
||||||
|
@ -893,27 +901,37 @@ void ConcurrentMark::checkpointRootsInitial() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Notice that in the next two methods, we actually leave the STS
|
* Notice that in the next two methods, we actually leave the STS
|
||||||
during the barrier sync and join it immediately afterwards. If we
|
* during the barrier sync and join it immediately afterwards. If we
|
||||||
do not do this, this then the following deadlock can occur: one
|
* do not do this, the following deadlock can occur: one thread could
|
||||||
thread could be in the barrier sync code, waiting for the other
|
* be in the barrier sync code, waiting for the other thread to also
|
||||||
thread to also sync up, whereas another one could be trying to
|
* sync up, whereas another one could be trying to yield, while also
|
||||||
yield, while also waiting for the other threads to sync up too.
|
* waiting for the other threads to sync up too.
|
||||||
|
*
|
||||||
Because the thread that does the sync barrier has left the STS, it
|
* Note, however, that this code is also used during remark and in
|
||||||
is possible to be suspended for a Full GC or an evacuation pause
|
* this case we should not attempt to leave / enter the STS, otherwise
|
||||||
could occur. This is actually safe, since the entering the sync
|
* we'll either hit an asseert (debug / fastdebug) or deadlock
|
||||||
barrier is one of the last things do_marking_step() does, and it
|
* (product). So we should only leave / enter the STS if we are
|
||||||
doesn't manipulate any data structures afterwards.
|
* operating concurrently.
|
||||||
*/
|
*
|
||||||
|
* Because the thread that does the sync barrier has left the STS, it
|
||||||
|
* is possible to be suspended for a Full GC or an evacuation pause
|
||||||
|
* could occur. This is actually safe, since the entering the sync
|
||||||
|
* barrier is one of the last things do_marking_step() does, and it
|
||||||
|
* doesn't manipulate any data structures afterwards.
|
||||||
|
*/
|
||||||
|
|
||||||
void ConcurrentMark::enter_first_sync_barrier(int task_num) {
|
void ConcurrentMark::enter_first_sync_barrier(int task_num) {
|
||||||
if (verbose_low())
|
if (verbose_low())
|
||||||
gclog_or_tty->print_cr("[%d] entering first barrier", task_num);
|
gclog_or_tty->print_cr("[%d] entering first barrier", task_num);
|
||||||
|
|
||||||
|
if (concurrent()) {
|
||||||
ConcurrentGCThread::stsLeave();
|
ConcurrentGCThread::stsLeave();
|
||||||
|
}
|
||||||
_first_overflow_barrier_sync.enter();
|
_first_overflow_barrier_sync.enter();
|
||||||
|
if (concurrent()) {
|
||||||
ConcurrentGCThread::stsJoin();
|
ConcurrentGCThread::stsJoin();
|
||||||
|
}
|
||||||
// at this point everyone should have synced up and not be doing any
|
// at this point everyone should have synced up and not be doing any
|
||||||
// more work
|
// more work
|
||||||
|
|
||||||
|
@ -923,7 +941,12 @@ void ConcurrentMark::enter_first_sync_barrier(int task_num) {
|
||||||
// let task 0 do this
|
// let task 0 do this
|
||||||
if (task_num == 0) {
|
if (task_num == 0) {
|
||||||
// task 0 is responsible for clearing the global data structures
|
// task 0 is responsible for clearing the global data structures
|
||||||
clear_marking_state();
|
// We should be here because of an overflow. During STW we should
|
||||||
|
// not clear the overflow flag since we rely on it being true when
|
||||||
|
// we exit this method to abort the pause and restart concurent
|
||||||
|
// marking.
|
||||||
|
clear_marking_state(concurrent() /* clear_overflow */);
|
||||||
|
force_overflow()->update();
|
||||||
|
|
||||||
if (PrintGC) {
|
if (PrintGC) {
|
||||||
gclog_or_tty->date_stamp(PrintGCDateStamps);
|
gclog_or_tty->date_stamp(PrintGCDateStamps);
|
||||||
|
@ -940,15 +963,45 @@ void ConcurrentMark::enter_second_sync_barrier(int task_num) {
|
||||||
if (verbose_low())
|
if (verbose_low())
|
||||||
gclog_or_tty->print_cr("[%d] entering second barrier", task_num);
|
gclog_or_tty->print_cr("[%d] entering second barrier", task_num);
|
||||||
|
|
||||||
|
if (concurrent()) {
|
||||||
ConcurrentGCThread::stsLeave();
|
ConcurrentGCThread::stsLeave();
|
||||||
|
}
|
||||||
_second_overflow_barrier_sync.enter();
|
_second_overflow_barrier_sync.enter();
|
||||||
|
if (concurrent()) {
|
||||||
ConcurrentGCThread::stsJoin();
|
ConcurrentGCThread::stsJoin();
|
||||||
|
}
|
||||||
// at this point everything should be re-initialised and ready to go
|
// at this point everything should be re-initialised and ready to go
|
||||||
|
|
||||||
if (verbose_low())
|
if (verbose_low())
|
||||||
gclog_or_tty->print_cr("[%d] leaving second barrier", task_num);
|
gclog_or_tty->print_cr("[%d] leaving second barrier", task_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef PRODUCT
|
||||||
|
void ForceOverflowSettings::init() {
|
||||||
|
_num_remaining = G1ConcMarkForceOverflow;
|
||||||
|
_force = false;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForceOverflowSettings::update() {
|
||||||
|
if (_num_remaining > 0) {
|
||||||
|
_num_remaining -= 1;
|
||||||
|
_force = true;
|
||||||
|
} else {
|
||||||
|
_force = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ForceOverflowSettings::should_force() {
|
||||||
|
if (_force) {
|
||||||
|
_force = false;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // !PRODUCT
|
||||||
|
|
||||||
void ConcurrentMark::grayRoot(oop p) {
|
void ConcurrentMark::grayRoot(oop p) {
|
||||||
HeapWord* addr = (HeapWord*) p;
|
HeapWord* addr = (HeapWord*) p;
|
||||||
// We can't really check against _heap_start and _heap_end, since it
|
// We can't really check against _heap_start and _heap_end, since it
|
||||||
|
@ -1117,6 +1170,7 @@ void ConcurrentMark::markFromRoots() {
|
||||||
_restart_for_overflow = false;
|
_restart_for_overflow = false;
|
||||||
|
|
||||||
size_t active_workers = MAX2((size_t) 1, parallel_marking_threads());
|
size_t active_workers = MAX2((size_t) 1, parallel_marking_threads());
|
||||||
|
force_overflow_conc()->init();
|
||||||
set_phase(active_workers, true /* concurrent */);
|
set_phase(active_workers, true /* concurrent */);
|
||||||
|
|
||||||
CMConcurrentMarkingTask markingTask(this, cmThread());
|
CMConcurrentMarkingTask markingTask(this, cmThread());
|
||||||
|
@ -2703,12 +2757,16 @@ void ConcurrentMark::oops_do(OopClosure* cl) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentMark::clear_marking_state() {
|
void ConcurrentMark::clear_marking_state(bool clear_overflow) {
|
||||||
_markStack.setEmpty();
|
_markStack.setEmpty();
|
||||||
_markStack.clear_overflow();
|
_markStack.clear_overflow();
|
||||||
_regionStack.setEmpty();
|
_regionStack.setEmpty();
|
||||||
_regionStack.clear_overflow();
|
_regionStack.clear_overflow();
|
||||||
|
if (clear_overflow) {
|
||||||
clear_has_overflown();
|
clear_has_overflown();
|
||||||
|
} else {
|
||||||
|
assert(has_overflown(), "pre-condition");
|
||||||
|
}
|
||||||
_finger = _heap_start;
|
_finger = _heap_start;
|
||||||
|
|
||||||
for (int i = 0; i < (int)_max_task_num; ++i) {
|
for (int i = 0; i < (int)_max_task_num; ++i) {
|
||||||
|
@ -4279,6 +4337,15 @@ void CMTask::do_marking_step(double time_target_ms,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are about to wrap up and go into termination, check if we
|
||||||
|
// should raise the overflow flag.
|
||||||
|
if (do_termination && !has_aborted()) {
|
||||||
|
if (_cm->force_overflow()->should_force()) {
|
||||||
|
_cm->set_has_overflown();
|
||||||
|
regular_clock_call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We still haven't aborted. Now, let's try to get into the
|
// We still haven't aborted. Now, let's try to get into the
|
||||||
// termination protocol.
|
// termination protocol.
|
||||||
if (do_termination && !has_aborted()) {
|
if (do_termination && !has_aborted()) {
|
||||||
|
|
|
@ -316,6 +316,19 @@ public:
|
||||||
void setEmpty() { _index = 0; clear_overflow(); }
|
void setEmpty() { _index = 0; clear_overflow(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ForceOverflowSettings VALUE_OBJ_CLASS_SPEC {
|
||||||
|
private:
|
||||||
|
#ifndef PRODUCT
|
||||||
|
uintx _num_remaining;
|
||||||
|
bool _force;
|
||||||
|
#endif // !defined(PRODUCT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
void init() PRODUCT_RETURN;
|
||||||
|
void update() PRODUCT_RETURN;
|
||||||
|
bool should_force() PRODUCT_RETURN_( return false; );
|
||||||
|
};
|
||||||
|
|
||||||
// this will enable a variety of different statistics per GC task
|
// this will enable a variety of different statistics per GC task
|
||||||
#define _MARKING_STATS_ 0
|
#define _MARKING_STATS_ 0
|
||||||
// this will enable the higher verbose levels
|
// this will enable the higher verbose levels
|
||||||
|
@ -462,6 +475,9 @@ protected:
|
||||||
|
|
||||||
WorkGang* _parallel_workers;
|
WorkGang* _parallel_workers;
|
||||||
|
|
||||||
|
ForceOverflowSettings _force_overflow_conc;
|
||||||
|
ForceOverflowSettings _force_overflow_stw;
|
||||||
|
|
||||||
void weakRefsWork(bool clear_all_soft_refs);
|
void weakRefsWork(bool clear_all_soft_refs);
|
||||||
|
|
||||||
void swapMarkBitMaps();
|
void swapMarkBitMaps();
|
||||||
|
@ -470,7 +486,7 @@ protected:
|
||||||
// task local ones; should be called during initial mark.
|
// task local ones; should be called during initial mark.
|
||||||
void reset();
|
void reset();
|
||||||
// It resets all the marking data structures.
|
// It resets all the marking data structures.
|
||||||
void clear_marking_state();
|
void clear_marking_state(bool clear_overflow = true);
|
||||||
|
|
||||||
// It should be called to indicate which phase we're in (concurrent
|
// It should be called to indicate which phase we're in (concurrent
|
||||||
// mark or remark) and how many threads are currently active.
|
// mark or remark) and how many threads are currently active.
|
||||||
|
@ -547,6 +563,22 @@ protected:
|
||||||
void enter_first_sync_barrier(int task_num);
|
void enter_first_sync_barrier(int task_num);
|
||||||
void enter_second_sync_barrier(int task_num);
|
void enter_second_sync_barrier(int task_num);
|
||||||
|
|
||||||
|
ForceOverflowSettings* force_overflow_conc() {
|
||||||
|
return &_force_overflow_conc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ForceOverflowSettings* force_overflow_stw() {
|
||||||
|
return &_force_overflow_stw;
|
||||||
|
}
|
||||||
|
|
||||||
|
ForceOverflowSettings* force_overflow() {
|
||||||
|
if (concurrent()) {
|
||||||
|
return force_overflow_conc();
|
||||||
|
} else {
|
||||||
|
return force_overflow_stw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Manipulation of the global mark stack.
|
// Manipulation of the global mark stack.
|
||||||
// Notice that the first mark_stack_push is CAS-based, whereas the
|
// Notice that the first mark_stack_push is CAS-based, whereas the
|
||||||
|
|
|
@ -311,7 +311,11 @@
|
||||||
\
|
\
|
||||||
develop(bool, G1ExitOnExpansionFailure, false, \
|
develop(bool, G1ExitOnExpansionFailure, false, \
|
||||||
"Raise a fatal VM exit out of memory failure in the event " \
|
"Raise a fatal VM exit out of memory failure in the event " \
|
||||||
" that heap expansion fails due to running out of swap.")
|
" that heap expansion fails due to running out of swap.") \
|
||||||
|
\
|
||||||
|
develop(uintx, G1ConcMarkForceOverflow, 0, \
|
||||||
|
"The number of times we'll force an overflow during " \
|
||||||
|
"concurrent marking")
|
||||||
|
|
||||||
G1_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG, DECLARE_MANAGEABLE_FLAG, DECLARE_PRODUCT_RW_FLAG)
|
G1_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG, DECLARE_MANAGEABLE_FLAG, DECLARE_PRODUCT_RW_FLAG)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue