6824570: ParNew: Fix memory leak introduced in 6819891
Allocate worker-local overflow stacks, introduced in 6819891, along with ParNewGeneration, rather than with the per-scavenge ParScanThreadState. Reviewed-by: jmasa
This commit is contained in:
parent
137a679446
commit
ce3f3161da
@ -34,10 +34,12 @@ ParScanThreadState::ParScanThreadState(Space* to_space_,
|
|||||||
Generation* old_gen_,
|
Generation* old_gen_,
|
||||||
int thread_num_,
|
int thread_num_,
|
||||||
ObjToScanQueueSet* work_queue_set_,
|
ObjToScanQueueSet* work_queue_set_,
|
||||||
|
GrowableArray<oop>** overflow_stack_set_,
|
||||||
size_t desired_plab_sz_,
|
size_t desired_plab_sz_,
|
||||||
ParallelTaskTerminator& term_) :
|
ParallelTaskTerminator& term_) :
|
||||||
_to_space(to_space_), _old_gen(old_gen_), _young_gen(gen_), _thread_num(thread_num_),
|
_to_space(to_space_), _old_gen(old_gen_), _young_gen(gen_), _thread_num(thread_num_),
|
||||||
_work_queue(work_queue_set_->queue(thread_num_)), _to_space_full(false),
|
_work_queue(work_queue_set_->queue(thread_num_)), _to_space_full(false),
|
||||||
|
_overflow_stack(overflow_stack_set_[thread_num_]),
|
||||||
_ageTable(false), // false ==> not the global age table, no perf data.
|
_ageTable(false), // false ==> not the global age table, no perf data.
|
||||||
_to_space_alloc_buffer(desired_plab_sz_),
|
_to_space_alloc_buffer(desired_plab_sz_),
|
||||||
_to_space_closure(gen_, this), _old_gen_closure(gen_, this),
|
_to_space_closure(gen_, this), _old_gen_closure(gen_, this),
|
||||||
@ -57,11 +59,6 @@ ParScanThreadState::ParScanThreadState(Space* to_space_,
|
|||||||
_start = os::elapsedTime();
|
_start = os::elapsedTime();
|
||||||
_old_gen_closure.set_generation(old_gen_);
|
_old_gen_closure.set_generation(old_gen_);
|
||||||
_old_gen_root_closure.set_generation(old_gen_);
|
_old_gen_root_closure.set_generation(old_gen_);
|
||||||
if (UseCompressedOops) {
|
|
||||||
_overflow_stack = new (ResourceObj::C_HEAP) GrowableArray<oop>(512, true);
|
|
||||||
} else {
|
|
||||||
_overflow_stack = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning( pop )
|
#pragma warning( pop )
|
||||||
@ -155,7 +152,7 @@ void ParScanThreadState::trim_queues(int max_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ParScanThreadState::take_from_overflow_stack() {
|
bool ParScanThreadState::take_from_overflow_stack() {
|
||||||
assert(UseCompressedOops, "Else should not call");
|
assert(ParGCUseLocalOverflow, "Else should not call");
|
||||||
assert(young_gen()->overflow_list() == NULL, "Error");
|
assert(young_gen()->overflow_list() == NULL, "Error");
|
||||||
ObjToScanQueue* queue = work_queue();
|
ObjToScanQueue* queue = work_queue();
|
||||||
GrowableArray<oop>* of_stack = overflow_stack();
|
GrowableArray<oop>* of_stack = overflow_stack();
|
||||||
@ -183,7 +180,7 @@ bool ParScanThreadState::take_from_overflow_stack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ParScanThreadState::push_on_overflow_stack(oop p) {
|
void ParScanThreadState::push_on_overflow_stack(oop p) {
|
||||||
assert(UseCompressedOops, "Else should not call");
|
assert(ParGCUseLocalOverflow, "Else should not call");
|
||||||
overflow_stack()->push(p);
|
overflow_stack()->push(p);
|
||||||
assert(young_gen()->overflow_list() == NULL, "Error");
|
assert(young_gen()->overflow_list() == NULL, "Error");
|
||||||
}
|
}
|
||||||
@ -260,6 +257,7 @@ public:
|
|||||||
ParNewGeneration& gen,
|
ParNewGeneration& gen,
|
||||||
Generation& old_gen,
|
Generation& old_gen,
|
||||||
ObjToScanQueueSet& queue_set,
|
ObjToScanQueueSet& queue_set,
|
||||||
|
GrowableArray<oop>** overflow_stacks_,
|
||||||
size_t desired_plab_sz,
|
size_t desired_plab_sz,
|
||||||
ParallelTaskTerminator& term);
|
ParallelTaskTerminator& term);
|
||||||
inline ParScanThreadState& thread_sate(int i);
|
inline ParScanThreadState& thread_sate(int i);
|
||||||
@ -282,6 +280,7 @@ private:
|
|||||||
ParScanThreadStateSet::ParScanThreadStateSet(
|
ParScanThreadStateSet::ParScanThreadStateSet(
|
||||||
int num_threads, Space& to_space, ParNewGeneration& gen,
|
int num_threads, Space& to_space, ParNewGeneration& gen,
|
||||||
Generation& old_gen, ObjToScanQueueSet& queue_set,
|
Generation& old_gen, ObjToScanQueueSet& queue_set,
|
||||||
|
GrowableArray<oop>** overflow_stack_set_,
|
||||||
size_t desired_plab_sz, ParallelTaskTerminator& term)
|
size_t desired_plab_sz, ParallelTaskTerminator& term)
|
||||||
: ResourceArray(sizeof(ParScanThreadState), num_threads),
|
: ResourceArray(sizeof(ParScanThreadState), num_threads),
|
||||||
_gen(gen), _next_gen(old_gen), _term(term),
|
_gen(gen), _next_gen(old_gen), _term(term),
|
||||||
@ -292,7 +291,7 @@ ParScanThreadStateSet::ParScanThreadStateSet(
|
|||||||
for (int i = 0; i < num_threads; ++i) {
|
for (int i = 0; i < num_threads; ++i) {
|
||||||
new ((ParScanThreadState*)_data + i)
|
new ((ParScanThreadState*)_data + i)
|
||||||
ParScanThreadState(&to_space, &gen, &old_gen, i, &queue_set,
|
ParScanThreadState(&to_space, &gen, &old_gen, i, &queue_set,
|
||||||
desired_plab_sz, term);
|
overflow_stack_set_, desired_plab_sz, term);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,6 +518,17 @@ ParNewGeneration(ReservedSpace rs, size_t initial_byte_size, int level)
|
|||||||
for (uint i2 = 0; i2 < ParallelGCThreads; i2++)
|
for (uint i2 = 0; i2 < ParallelGCThreads; i2++)
|
||||||
_task_queues->queue(i2)->initialize();
|
_task_queues->queue(i2)->initialize();
|
||||||
|
|
||||||
|
_overflow_stacks = NEW_C_HEAP_ARRAY(GrowableArray<oop>*, ParallelGCThreads);
|
||||||
|
guarantee(_overflow_stacks != NULL, "Overflow stack set allocation failure");
|
||||||
|
for (uint i = 0; i < ParallelGCThreads; i++) {
|
||||||
|
if (ParGCUseLocalOverflow) {
|
||||||
|
_overflow_stacks[i] = new (ResourceObj::C_HEAP) GrowableArray<oop>(512, true);
|
||||||
|
guarantee(_overflow_stacks[i] != NULL, "Overflow Stack allocation failure.");
|
||||||
|
} else {
|
||||||
|
_overflow_stacks[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (UsePerfData) {
|
if (UsePerfData) {
|
||||||
EXCEPTION_MARK;
|
EXCEPTION_MARK;
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
@ -784,7 +794,7 @@ void ParNewGeneration::collect(bool full,
|
|||||||
ParallelTaskTerminator _term(workers->total_workers(), task_queues());
|
ParallelTaskTerminator _term(workers->total_workers(), task_queues());
|
||||||
ParScanThreadStateSet thread_state_set(workers->total_workers(),
|
ParScanThreadStateSet thread_state_set(workers->total_workers(),
|
||||||
*to(), *this, *_next_gen, *task_queues(),
|
*to(), *this, *_next_gen, *task_queues(),
|
||||||
desired_plab_sz(), _term);
|
_overflow_stacks, desired_plab_sz(), _term);
|
||||||
|
|
||||||
ParNewGenTask tsk(this, _next_gen, reserved().end(), &thread_state_set);
|
ParNewGenTask tsk(this, _next_gen, reserved().end(), &thread_state_set);
|
||||||
int n_workers = workers->total_workers();
|
int n_workers = workers->total_workers();
|
||||||
@ -1238,11 +1248,12 @@ bool ParNewGeneration::should_simulate_overflow() {
|
|||||||
#define BUSY (oop(0x1aff1aff))
|
#define BUSY (oop(0x1aff1aff))
|
||||||
void ParNewGeneration::push_on_overflow_list(oop from_space_obj, ParScanThreadState* par_scan_state) {
|
void ParNewGeneration::push_on_overflow_list(oop from_space_obj, ParScanThreadState* par_scan_state) {
|
||||||
assert(is_in_reserved(from_space_obj), "Should be from this generation");
|
assert(is_in_reserved(from_space_obj), "Should be from this generation");
|
||||||
if (UseCompressedOops) {
|
if (ParGCUseLocalOverflow) {
|
||||||
// In the case of compressed oops, we use a private, not-shared
|
// In the case of compressed oops, we use a private, not-shared
|
||||||
// overflow stack.
|
// overflow stack.
|
||||||
par_scan_state->push_on_overflow_stack(from_space_obj);
|
par_scan_state->push_on_overflow_stack(from_space_obj);
|
||||||
} else {
|
} else {
|
||||||
|
assert(!UseCompressedOops, "Error");
|
||||||
// if the object has been forwarded to itself, then we cannot
|
// if the object has been forwarded to itself, then we cannot
|
||||||
// use the klass pointer for the linked list. Instead we have
|
// use the klass pointer for the linked list. Instead we have
|
||||||
// to allocate an oopDesc in the C-Heap and use that for the linked list.
|
// to allocate an oopDesc in the C-Heap and use that for the linked list.
|
||||||
@ -1275,9 +1286,10 @@ void ParNewGeneration::push_on_overflow_list(oop from_space_obj, ParScanThreadSt
|
|||||||
bool ParNewGeneration::take_from_overflow_list(ParScanThreadState* par_scan_state) {
|
bool ParNewGeneration::take_from_overflow_list(ParScanThreadState* par_scan_state) {
|
||||||
bool res;
|
bool res;
|
||||||
|
|
||||||
if (UseCompressedOops) {
|
if (ParGCUseLocalOverflow) {
|
||||||
res = par_scan_state->take_from_overflow_stack();
|
res = par_scan_state->take_from_overflow_stack();
|
||||||
} else {
|
} else {
|
||||||
|
assert(!UseCompressedOops, "Error");
|
||||||
res = take_from_overflow_list_work(par_scan_state);
|
res = take_from_overflow_list_work(par_scan_state);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@ -1305,6 +1317,7 @@ bool ParNewGeneration::take_from_overflow_list_work(ParScanThreadState* par_scan
|
|||||||
(size_t)ParGCDesiredObjsFromOverflowList);
|
(size_t)ParGCDesiredObjsFromOverflowList);
|
||||||
|
|
||||||
assert(par_scan_state->overflow_stack() == NULL, "Error");
|
assert(par_scan_state->overflow_stack() == NULL, "Error");
|
||||||
|
assert(!UseCompressedOops, "Error");
|
||||||
if (_overflow_list == NULL) return false;
|
if (_overflow_list == NULL) return false;
|
||||||
|
|
||||||
// Otherwise, there was something there; try claiming the list.
|
// Otherwise, there was something there; try claiming the list.
|
||||||
|
@ -116,7 +116,9 @@ class ParScanThreadState {
|
|||||||
|
|
||||||
ParScanThreadState(Space* to_space_, ParNewGeneration* gen_,
|
ParScanThreadState(Space* to_space_, ParNewGeneration* gen_,
|
||||||
Generation* old_gen_, int thread_num_,
|
Generation* old_gen_, int thread_num_,
|
||||||
ObjToScanQueueSet* work_queue_set_, size_t desired_plab_sz_,
|
ObjToScanQueueSet* work_queue_set_,
|
||||||
|
GrowableArray<oop>** overflow_stack_set_,
|
||||||
|
size_t desired_plab_sz_,
|
||||||
ParallelTaskTerminator& term_);
|
ParallelTaskTerminator& term_);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -296,9 +298,12 @@ class ParNewGeneration: public DefNewGeneration {
|
|||||||
char pad[64 - sizeof(ObjToScanQueue)]; // prevent false sharing
|
char pad[64 - sizeof(ObjToScanQueue)]; // prevent false sharing
|
||||||
};
|
};
|
||||||
|
|
||||||
// The per-thread work queues, available here for stealing.
|
// The per-worker-thread work queues
|
||||||
ObjToScanQueueSet* _task_queues;
|
ObjToScanQueueSet* _task_queues;
|
||||||
|
|
||||||
|
// Per-worker-thread local overflow stacks
|
||||||
|
GrowableArray<oop>** _overflow_stacks;
|
||||||
|
|
||||||
// Desired size of survivor space plab's
|
// Desired size of survivor space plab's
|
||||||
PLABStats _plab_stats;
|
PLABStats _plab_stats;
|
||||||
|
|
||||||
|
@ -971,7 +971,7 @@ void Arguments::set_parnew_gc_flags() {
|
|||||||
} else {
|
} else {
|
||||||
no_shared_spaces();
|
no_shared_spaces();
|
||||||
|
|
||||||
// By default YoungPLABSize and OldPLABSize are set to 4096 and 1024 correspondinly,
|
// By default YoungPLABSize and OldPLABSize are set to 4096 and 1024 respectively,
|
||||||
// these settings are default for Parallel Scavenger. For ParNew+Tenured configuration
|
// these settings are default for Parallel Scavenger. For ParNew+Tenured configuration
|
||||||
// we set them to 1024 and 1024.
|
// we set them to 1024 and 1024.
|
||||||
// See CR 6362902.
|
// See CR 6362902.
|
||||||
@ -987,6 +987,16 @@ void Arguments::set_parnew_gc_flags() {
|
|||||||
if (AlwaysTenure) {
|
if (AlwaysTenure) {
|
||||||
FLAG_SET_CMDLINE(intx, MaxTenuringThreshold, 0);
|
FLAG_SET_CMDLINE(intx, MaxTenuringThreshold, 0);
|
||||||
}
|
}
|
||||||
|
// When using compressed oops, we use local overflow stacks,
|
||||||
|
// rather than using a global overflow list chained through
|
||||||
|
// the klass word of the object's pre-image.
|
||||||
|
if (UseCompressedOops && !ParGCUseLocalOverflow) {
|
||||||
|
if (!FLAG_IS_DEFAULT(ParGCUseLocalOverflow)) {
|
||||||
|
warning("Forcing +ParGCUseLocalOverflow: needed if using compressed references");
|
||||||
|
}
|
||||||
|
FLAG_SET_DEFAULT(ParGCUseLocalOverflow, true);
|
||||||
|
}
|
||||||
|
assert(ParGCUseLocalOverflow || !UseCompressedOops, "Error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1316,8 +1316,11 @@ class CommandLineFlags {
|
|||||||
product(intx, ParGCArrayScanChunk, 50, \
|
product(intx, ParGCArrayScanChunk, 50, \
|
||||||
"Scan a subset and push remainder, if array is bigger than this") \
|
"Scan a subset and push remainder, if array is bigger than this") \
|
||||||
\
|
\
|
||||||
|
product(bool, ParGCUseLocalOverflow, false, \
|
||||||
|
"Instead of a global overflow list, use local overflow stacks") \
|
||||||
|
\
|
||||||
product(bool, ParGCTrimOverflow, true, \
|
product(bool, ParGCTrimOverflow, true, \
|
||||||
"Eagerly trim the overflow lists (useful for UseCompressedOops") \
|
"Eagerly trim the local overflow lists (when ParGCUseLocalOverflow") \
|
||||||
\
|
\
|
||||||
notproduct(bool, ParGCWorkQueueOverflowALot, false, \
|
notproduct(bool, ParGCWorkQueueOverflowALot, false, \
|
||||||
"Whether we should simulate work queue overflow in ParNew") \
|
"Whether we should simulate work queue overflow in ParNew") \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user