Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JDK-8260332: ParallelGC: Cooperative pretouch for oldgen expansion #2976

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter
Filter file types
Jump to
Jump to file
Failed to load files.

Always

Just for now

@@ -34,6 +34,7 @@
#include "gc/g1/heapRegionSet.inline.hpp"
#include "gc/g1/heapRegionType.hpp"
#include "gc/shared/tlab_globals.hpp"
#include "gc/shared/pretouchTask.hpp"
#include "utilities/align.hpp"

G1Allocator::G1Allocator(G1CollectedHeap* heap) :
@@ -271,12 +272,30 @@ HeapWord* G1Allocator::old_attempt_allocation(size_t min_word_size,
desired_word_size,
actual_word_size);
if (result == NULL && !old_is_full()) {
MutexLocker x(FreeList_lock, Mutex::_no_safepoint_check_flag);
result = old_gc_alloc_region()->attempt_allocation_locked(min_word_size,
bool is_locked = false;
PretouchTaskCoordinator *task_coordinator = PretouchTaskCoordinator::get_task_coordinator();
while (true) {
if (UseMultithreadedPretouchForOldGen) {
is_locked = FreeList_lock->try_lock();
} else {
FreeList_lock->lock();
is_locked = true;
}
if (is_locked) {
task_coordinator->release_set_task_notready();
result = old_gc_alloc_region()->attempt_allocation_locked(min_word_size,
desired_word_size,
actual_word_size);
if (result == NULL) {
set_old_full();
task_coordinator->release_set_task_done();
if (result == NULL) {
set_old_full();
}
FreeList_lock->unlock();
break;
} else {
// Lets help expanding thread to pretouch the memory.
task_coordinator->worker_wait_for_task();
}
}
}
return result;
@@ -576,8 +576,7 @@ void MutableNUMASpace::initialize(MemRegion mr,
bool clear_space,
bool mangle_space,
bool setup_pages,
WorkGang* pretouch_gang,
PretouchTask* pretouch_task) {
WorkGang* pretouch_gang) {
assert(clear_space, "Reallocation will destroy data!");
assert(lgrp_spaces()->length() > 0, "There should be at least one space");

@@ -201,8 +201,7 @@ class MutableNUMASpace : public MutableSpace {
bool clear_space,
bool mangle_space,
bool setup_pages = SetupPages,
WorkGang* pretouch_gang = NULL,
PretouchTask* pretouch_task = NULL);
WorkGang* pretouch_gang = NULL);
// Update space layout if necessary. Do all adaptive resizing job.
virtual void update();
// Update allocation rate averages.
@@ -31,7 +31,6 @@
#include "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/osThread.hpp"
#include "runtime/thread.hpp"
#include "utilities/align.hpp"
#include "utilities/macros.hpp"
@@ -73,8 +72,7 @@ void MutableSpace::initialize(MemRegion mr,
bool clear_space,
bool mangle_space,
bool setup_pages,
WorkGang* pretouch_gang,
PretouchTask* pretouch_task) {
WorkGang* pretouch_gang) {

assert(Universe::on_page_boundary(mr.start()) && Universe::on_page_boundary(mr.end()),
"invalid space boundaries");
@@ -123,44 +121,11 @@ void MutableSpace::initialize(MemRegion mr,
if (AlwaysPreTouch) {
size_t page_size = UseLargePages ? os::large_page_size() : os::vm_page_size();

// Old-gen gets expanded during promotion failure and this GC thread (not
// VM thread) is already executing a GC task so cant call run_task.
// "pretouch_task" is a special case for old-gen to make pretouch
// multi-threaded and is part of PSOldGen object and shared between the
// threads (expanding thread and threads waiting for expansion).
if (pretouch_gang == NULL) {
PretouchTask::pretouch("ParallelGC PreTouch head", (char*)head.start(), (char*)head.end(),
page_size, pretouch_gang);

assert(head.start() == head.end(), " old gen is expanding from head side also.");

// Update the object with tail part for pretouch.
pretouch_task->reinitialize((char*)tail.start(), (char*)tail.end());

size_t total_bytes = pointer_delta(tail.end(), tail.start(), sizeof(char));
size_t chunk_size =0;
PretouchTask::setup_chunk_size_and_page_size(chunk_size, page_size);
size_t num_chunks = (total_bytes + chunk_size - 1) / chunk_size;
log_debug(gc, heap)("Running %s with " SIZE_FORMAT " work units pre-touching " SIZE_FORMAT "B.",
pretouch_task->name(), num_chunks, total_bytes);

OrderAccess::storestore();
// Mark Pretouch task ready here and continue. The other threads
// waiting to expand old-gen will join from PSOldGen::expand_for_allocate
// function for pretouch work.
pretouch_task->set_task_ready();
pretouch_task->work(Thread::current()->osthread()->thread_id());

// Wait for task to be finished by participating threads.
while(!pretouch_task->is_task_done()) {
SpinPause();
}
} else {

PretouchTask::pretouch("ParallelGC PreTouch head", (char*)head.start(), (char*)head.end(),
page_size, pretouch_gang);

PretouchTask::pretouch("ParallelGC PreTouch tail", (char*)tail.start(), (char*)tail.end(),
page_size, pretouch_gang);
}
PretouchTask::pretouch("ParallelGC PreTouch tail", (char*)tail.start(), (char*)tail.end(),
page_size, pretouch_gang);
}

// Remember where we stopped so that we can continue later.
@@ -33,7 +33,6 @@
#include "utilities/macros.hpp"

class WorkGang;
class PretouchTask;

// A MutableSpace supports the concept of allocation. This includes the
// concepts that a space may be only partially full, and the query methods
@@ -103,8 +102,7 @@ class MutableSpace: public CHeapObj<mtGC> {
bool clear_space,
bool mangle_space,
bool setup_pages = SetupPages,
WorkGang* pretouch_gang = NULL,
PretouchTask* pretouch_task = NULL);
WorkGang* pretouch_gang = NULL);

virtual void clear(bool mangle_space);
virtual void update() { }
@@ -130,17 +130,6 @@ void PSOldGen::initialize_work(const char* perf_data_name, int level) {

// Update the start_array
start_array()->set_covered_region(cmr);

size_t chunk_size = 0;
size_t page_size = (size_t)os::vm_page_size();
PretouchTask::setup_chunk_size_and_page_size(chunk_size, page_size);

_pretouch = new PretouchTask("pretouch for oldgen expansion",
(char*)object_space()->end(),
(char*)object_space()->end(),
page_size,
chunk_size);
_pretouch->set_task_notready();
}

void PSOldGen::initialize_performance_counters(const char* perf_data_name, int level) {
@@ -195,8 +184,14 @@ bool PSOldGen::expand_for_allocate(size_t word_size) {
bool result = true;
{
bool is_locked = false;
PretouchTaskCoordinator *task_coordinator = PretouchTaskCoordinator::get_task_coordinator();
while(true) {
is_locked = ExpandHeap_lock->try_lock();
if (UseMultithreadedPretouchForOldGen) {
is_locked = ExpandHeap_lock->try_lock();
} else {
ExpandHeap_lock->lock();
is_locked = true;
}
// Avoid "expand storms" by rechecking available space after obtaining
// the lock, because another thread may have already made sufficient
// space available. If insufficient space available, that will remain
@@ -205,30 +200,23 @@ bool PSOldGen::expand_for_allocate(size_t word_size) {
// expand. That's okay, we'll just try expanding again.
//
// Todo:
// Thread which holds the lock can expand once for all thre threads and
// this will be win-win for all threads.
// Thread which holds the lock can expand once for all the threads and
// this will be win-win for all the threads.
if (is_locked) {
if (object_space()->needs_expand(word_size)) {
// Marking not ready makes other threads to Spin in loop.
pretouch()->set_task_notready();
// Marking not ready makes other threads to spin in loop.
task_coordinator->release_set_task_notready();
result = expand(word_size*HeapWordSize);
pretouch()->set_task_done();
task_coordinator->release_set_task_done();
}

assert (pretouch()->is_task_done(), "Task should be done at this point");
assert (task_coordinator->is_task_done_acquire(), "Task should be done at this point");
ExpandHeap_lock->unlock();
break;

} else {
// Lets help expanding thread to pretouch the memory.
while (!pretouch()->is_task_done()) {
if (pretouch()->is_task_ready()) {
pretouch()->work(Thread::current()->osthread()->thread_id());
} else {
SpinPause();
}
}
SpinPause();
task_coordinator->worker_wait_for_task();
}
}
}
@@ -409,8 +397,7 @@ void PSOldGen::post_resize() {
SpaceDecorator::DontClear,
SpaceDecorator::DontMangle,
MutableSpace::SetupPages,
workers,
pretouch());
workers);

assert(new_word_size == heap_word_size(object_space()->capacity_in_bytes()),
"Sanity");
@@ -32,8 +32,6 @@
#include "gc/parallel/spaceCounters.hpp"
#include "runtime/safepoint.hpp"

class PretouchTask;

class PSOldGen : public CHeapObj<mtGC> {
friend class VMStructs;

@@ -46,7 +44,6 @@ class PSOldGen : public CHeapObj<mtGC> {
// Performance Counters
PSGenerationCounters* _gen_counters;
SpaceCounters* _space_counters;
PretouchTask* _pretouch; // Used when old gen resized during scavenging.

// Sizing information, in bytes, set in constructor
const size_t _min_gen_size;
@@ -117,7 +114,6 @@ class PSOldGen : public CHeapObj<mtGC> {
MutableSpace* object_space() const { return _object_space; }
ObjectStartArray* start_array() { return &_start_array; }
PSVirtualSpace* virtual_space() const { return _virtual_space;}
PretouchTask* pretouch() { return _pretouch; }

// Has the generation been successfully allocated?
bool is_allocated();
@@ -196,6 +196,9 @@
"bigger than this") \
range(1, max_jint/3) \
\
product(bool, UseMultithreadedPretouchForOldGen, false, \
"Oldgen expands during promotional failure and pages are touched" \
"with single thread. This option makes it multi-threaded" ) \
\
product(bool, AlwaysPreTouch, false, \
"Force all freshly committed pages to be pre-touched") \
ProTip! Use n and p to navigate between commits in a pull request.