6711316: Open source the Garbage-First garbage collector

First mercurial integration of the code for the Garbage-First garbage collector.

Reviewed-by: apetrusenko, iveresov, jmasa, sgoldman, tonyp, ysr
This commit is contained in:
Y. Srinivas Ramakrishna 2008-06-05 15:57:56 -07:00
parent 39463bb3fc
commit 18f3386a98
215 changed files with 36088 additions and 1249 deletions

View file

@ -0,0 +1,189 @@
/*
* Copyright 2001-2007 Sun Microsystems, Inc. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
# include "incls/_precompiled.incl"
# include "incls/_coTracker.cpp.incl"
COTracker* COTracker::_head = NULL;
double COTracker::_cpu_number = -1.0;
void
COTracker::resetPeriod(double now_sec, double vnow_sec) {
guarantee( _enabled, "invariant" );
_period_start_time_sec = now_sec;
_period_start_vtime_sec = vnow_sec;
}
void
COTracker::setConcOverhead(double time_stamp_sec,
double conc_overhead) {
guarantee( _enabled, "invariant" );
_conc_overhead = conc_overhead;
_time_stamp_sec = time_stamp_sec;
if (conc_overhead > 0.001)
_conc_overhead_seq.add(conc_overhead);
}
void
COTracker::reset(double starting_conc_overhead) {
guarantee( _enabled, "invariant" );
double now_sec = os::elapsedTime();
setConcOverhead(now_sec, starting_conc_overhead);
}
void
COTracker::start() {
guarantee( _enabled, "invariant" );
resetPeriod(os::elapsedTime(), os::elapsedVTime());
}
void
COTracker::update(bool force_end) {
assert( _enabled, "invariant" );
double end_time_sec = os::elapsedTime();
double elapsed_time_sec = end_time_sec - _period_start_time_sec;
if (force_end || elapsed_time_sec > _update_period_sec) {
// reached the end of the period
double end_vtime_sec = os::elapsedVTime();
double elapsed_vtime_sec = end_vtime_sec - _period_start_vtime_sec;
double conc_overhead = elapsed_vtime_sec / elapsed_time_sec;
setConcOverhead(end_time_sec, conc_overhead);
resetPeriod(end_time_sec, end_vtime_sec);
}
}
void
COTracker::updateForSTW(double start_sec, double end_sec) {
if (!_enabled)
return;
// During a STW pause, no concurrent GC thread has done any
// work. So, we can safely adjust the start of the current period by
// adding the duration of the STW pause to it, so that the STW pause
// doesn't affect the reading of the concurrent overhead (it's
// basically like excluding the time of the STW pause from the
// concurrent overhead calculation).
double stw_duration_sec = end_sec - start_sec;
guarantee( stw_duration_sec > 0.0, "invariant" );
if (outOfDate(start_sec))
_conc_overhead = 0.0;
else
_time_stamp_sec = end_sec;
_period_start_time_sec += stw_duration_sec;
_conc_overhead_seq = NumberSeq();
guarantee( os::elapsedTime() > _period_start_time_sec, "invariant" );
}
double
COTracker::predConcOverhead() {
if (_enabled) {
// tty->print(" %1.2lf", _conc_overhead_seq.maximum());
return _conc_overhead_seq.maximum();
} else {
// tty->print(" DD");
return 0.0;
}
}
void
COTracker::resetPred() {
_conc_overhead_seq = NumberSeq();
}
COTracker::COTracker(int group)
: _enabled(false),
_group(group),
_period_start_time_sec(-1.0),
_period_start_vtime_sec(-1.0),
_conc_overhead(-1.0),
_time_stamp_sec(-1.0),
_next(NULL) {
// GCOverheadReportingPeriodMS indicates how frequently the
// concurrent overhead will be recorded by the GC Overhead
// Reporter. We want to take readings less often than that. If we
// took readings more often than some of them might be lost.
_update_period_sec = ((double) GCOverheadReportingPeriodMS) / 1000.0 * 1.25;
_next = _head;
_head = this;
if (_cpu_number < 0.0)
_cpu_number = (double) os::processor_count();
}
// statics
void
COTracker::updateAllForSTW(double start_sec, double end_sec) {
for (COTracker* curr = _head; curr != NULL; curr = curr->_next) {
curr->updateForSTW(start_sec, end_sec);
}
}
double
COTracker::totalConcOverhead(double now_sec) {
double total_conc_overhead = 0.0;
for (COTracker* curr = _head; curr != NULL; curr = curr->_next) {
double conc_overhead = curr->concOverhead(now_sec);
total_conc_overhead += conc_overhead;
}
return total_conc_overhead;
}
double
COTracker::totalConcOverhead(double now_sec,
size_t group_num,
double* co_per_group) {
double total_conc_overhead = 0.0;
for (size_t i = 0; i < group_num; ++i)
co_per_group[i] = 0.0;
for (COTracker* curr = _head; curr != NULL; curr = curr->_next) {
size_t group = curr->_group;
assert( 0 <= group && group < group_num, "invariant" );
double conc_overhead = curr->concOverhead(now_sec);
co_per_group[group] += conc_overhead;
total_conc_overhead += conc_overhead;
}
return total_conc_overhead;
}
double
COTracker::totalPredConcOverhead() {
double total_pred_conc_overhead = 0.0;
for (COTracker* curr = _head; curr != NULL; curr = curr->_next) {
total_pred_conc_overhead += curr->predConcOverhead();
curr->resetPred();
}
return total_pred_conc_overhead / _cpu_number;
}

View file

@ -0,0 +1,181 @@
/*
* Copyright 2001-2007 Sun Microsystems, Inc. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
// COTracker keeps track of the concurrent overhead of a GC thread.
// A thread that needs to be tracked must, itself, start up its
// tracker with the start() method and then call the update() method
// at regular intervals. What the tracker does is to calculate the
// concurrent overhead of a process at a given update period. The
// tracker starts and when is detects that it has exceeded the given
// period, it calculates the duration of the period in wall-clock time
// and the duration of the period in vtime (i.e. how much time the
// concurrent processes really took up during this period). The ratio
// of the latter over the former is the concurrent overhead of that
// process for that period over a single CPU. This overhead is stored
// on the tracker, "timestamped" with the wall-clock time of the end
// of the period. When the concurrent overhead of this process needs
// to be queried, this last "reading" provides a good approximation
// (we assume that the concurrent overhead of a particular thread
// stays largely constant over time). The timestamp is necessary to
// detect when the process has stopped working and the recorded
// reading hasn't been updated for some time.
// Each concurrent GC thread is considered to be part of a "group"
// (i.e. any available concurrent marking threads are part of the
// "concurrent marking thread group"). A COTracker is associated with
// a single group at construction-time. It's up to each collector to
// decide how groups will be mapped to such an id (ids should start
// from 0 and be consecutive; there's a hardcoded max group num
// defined on the GCOverheadTracker class). The notion of a group has
// been introduced to be able to identify how much overhead was
// imposed by each group, instead of getting a single value that
// covers all concurrent overhead.
class COTracker {
private:
// It indicates whether this tracker is enabled or not. When the
// tracker is disabled, then it returns 0.0 as the latest concurrent
// overhead and several methods (reset, start, and update) are not
// supposed to be called on it. This enabling / disabling facility
// is really provided to make a bit more explicit in the code when a
// particulary tracker of a processes that doesn't run all the time
// (e.g. concurrent marking) is supposed to be used and not it's not.
bool _enabled;
// The ID of the group associated with this tracker.
int _group;
// The update period of the tracker. A new value for the concurrent
// overhead of the associated process will be made at intervals no
// smaller than this.
double _update_period_sec;
// The start times (both wall-block time and vtime) of the current
// interval.
double _period_start_time_sec;
double _period_start_vtime_sec;
// Number seq of the concurrent overhead readings within a period
NumberSeq _conc_overhead_seq;
// The latest reading of the concurrent overhead (over a single CPU)
// imposed by the associated concurrent thread, made available at
// the indicated wall-clock time.
double _conc_overhead;
double _time_stamp_sec;
// The number of CPUs that the host machine has (for convenience
// really, as we'd have to keep translating it into a double)
static double _cpu_number;
// Fields that keep a list of all trackers created. This is useful,
// since it allows us to sum up the concurrent overhead without
// having to write code for a specific collector to broadcast a
// request to all its concurrent processes.
COTracker* _next;
static COTracker* _head;
// It indicates that a new period is starting by updating the
// _period_start_time_sec and _period_start_vtime_sec fields.
void resetPeriod(double now_sec, double vnow_sec);
// It updates the latest concurrent overhead reading, taken at a
// given wall-clock time.
void setConcOverhead(double time_stamp_sec, double conc_overhead);
// It determines whether the time stamp of the latest concurrent
// overhead reading is out of date or not.
bool outOfDate(double now_sec) {
// The latest reading is considered out of date, if it was taken
// 1.2x the update period.
return (now_sec - _time_stamp_sec) > 1.2 * _update_period_sec;
}
public:
// The constructor which associates the tracker with a group ID.
COTracker(int group);
// Methods to enable / disable the tracker and query whether it is enabled.
void enable() { _enabled = true; }
void disable() { _enabled = false; }
bool enabled() { return _enabled; }
// It resets the tracker and sets concurrent overhead reading to be
// the given parameter and the associated time stamp to be now.
void reset(double starting_conc_overhead = 0.0);
// The tracker starts tracking. IT should only be called from the
// concurrent thread that is tracked by this tracker.
void start();
// It updates the tracker and, if the current period is longer than
// the update period, the concurrent overhead reading will be
// updated. force_end being true indicates that it's the last call
// to update() by this process before the tracker is disabled (the
// tracker can be re-enabled later if necessary). It should only be
// called from the concurrent thread that is tracked by this tracker
// and while the thread has joined the STS.
void update(bool force_end = false);
// It adjusts the contents of the tracker to take into account a STW
// pause.
void updateForSTW(double start_sec, double end_sec);
// It returns the last concurrent overhead reading over a single
// CPU. If the reading is out of date, or the tracker is disabled,
// it returns 0.0.
double concCPUOverhead(double now_sec) {
if (!_enabled || outOfDate(now_sec))
return 0.0;
else
return _conc_overhead;
}
// It returns the last concurrent overhead reading over all CPUs
// that the host machine has. If the reading is out of date, or the
// tracker is disabled, it returns 0.0.
double concOverhead(double now_sec) {
return concCPUOverhead(now_sec) / _cpu_number;
}
double predConcOverhead();
void resetPred();
// statics
// It notifies all trackers about a STW pause.
static void updateAllForSTW(double start_sec, double end_sec);
// It returns the sum of the concurrent overhead readings of all
// available (and enabled) trackers for the given time stamp. The
// overhead is over all the CPUs of the host machine.
static double totalConcOverhead(double now_sec);
// Like the previous method, but it also sums up the overheads per
// group number. The length of the co_per_group array must be at
// least as large group_num
static double totalConcOverhead(double now_sec,
size_t group_num,
double* co_per_group);
static double totalPredConcOverhead();
};

View file

@ -0,0 +1,314 @@
/*
* Copyright 2001-2005 Sun Microsystems, Inc. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
// CopyrightVersion 1.2
# include "incls/_precompiled.incl"
# include "incls/_concurrentGCThread.cpp.incl"
bool ConcurrentGCThread::_should_terminate = false;
bool ConcurrentGCThread::_has_terminated = false;
int ConcurrentGCThread::_CGC_flag = CGC_nil;
SuspendibleThreadSet ConcurrentGCThread::_sts;
ConcurrentGCThread::ConcurrentGCThread() {
_sts.initialize();
};
void ConcurrentGCThread::stopWorldAndDo(VoidClosure* op) {
MutexLockerEx x(Heap_lock,
Mutex::_no_safepoint_check_flag);
// warning("CGC: about to try stopping world");
SafepointSynchronize::begin();
// warning("CGC: successfully stopped world");
op->do_void();
SafepointSynchronize::end();
// warning("CGC: successfully restarted world");
}
void ConcurrentGCThread::safepoint_synchronize() {
_sts.suspend_all();
}
void ConcurrentGCThread::safepoint_desynchronize() {
_sts.resume_all();
}
void ConcurrentGCThread::create_and_start() {
if (os::create_thread(this, os::cgc_thread)) {
// XXX: need to set this to low priority
// unless "agressive mode" set; priority
// should be just less than that of VMThread.
os::set_priority(this, NearMaxPriority);
if (!_should_terminate && !DisableStartThread) {
os::start_thread(this);
}
}
}
void ConcurrentGCThread::initialize_in_thread() {
this->record_stack_base_and_size();
this->initialize_thread_local_storage();
this->set_active_handles(JNIHandleBlock::allocate_block());
// From this time Thread::current() should be working.
assert(this == Thread::current(), "just checking");
}
void ConcurrentGCThread::wait_for_universe_init() {
MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag);
while (!is_init_completed() && !_should_terminate) {
CGC_lock->wait(Mutex::_no_safepoint_check_flag, 200);
}
}
void ConcurrentGCThread::terminate() {
// Signal that it is terminated
{
MutexLockerEx mu(Terminator_lock,
Mutex::_no_safepoint_check_flag);
_has_terminated = true;
Terminator_lock->notify();
}
// Thread destructor usually does this..
ThreadLocalStorage::set_thread(NULL);
}
void SuspendibleThreadSet::initialize_work() {
MutexLocker x(STS_init_lock);
if (!_initialized) {
_m = new Monitor(Mutex::leaf,
"SuspendibleThreadSetLock", true);
_async = 0;
_async_stop = false;
_async_stopped = 0;
_initialized = true;
}
}
void SuspendibleThreadSet::join() {
initialize();
MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag);
while (_async_stop) _m->wait(Mutex::_no_safepoint_check_flag);
_async++;
assert(_async > 0, "Huh.");
}
void SuspendibleThreadSet::leave() {
assert(_initialized, "Must be initialized.");
MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag);
_async--;
assert(_async >= 0, "Huh.");
if (_async_stop) _m->notify_all();
}
void SuspendibleThreadSet::yield(const char* id) {
assert(_initialized, "Must be initialized.");
if (_async_stop) {
MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag);
if (_async_stop) {
_async_stopped++;
assert(_async_stopped > 0, "Huh.");
if (_async_stopped == _async) {
if (ConcGCYieldTimeout > 0) {
double now = os::elapsedTime();
guarantee((now - _suspend_all_start) * 1000.0 <
(double)ConcGCYieldTimeout,
"Long delay; whodunit?");
}
}
_m->notify_all();
while (_async_stop) _m->wait(Mutex::_no_safepoint_check_flag);
_async_stopped--;
assert(_async >= 0, "Huh");
_m->notify_all();
}
}
}
void SuspendibleThreadSet::suspend_all() {
initialize(); // If necessary.
if (ConcGCYieldTimeout > 0) {
_suspend_all_start = os::elapsedTime();
}
MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag);
assert(!_async_stop, "Only one at a time.");
_async_stop = true;
while (_async_stopped < _async) _m->wait(Mutex::_no_safepoint_check_flag);
}
void SuspendibleThreadSet::resume_all() {
assert(_initialized, "Must be initialized.");
MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag);
assert(_async_stopped == _async, "Huh.");
_async_stop = false;
_m->notify_all();
}
static void _sltLoop(JavaThread* thread, TRAPS) {
SurrogateLockerThread* slt = (SurrogateLockerThread*)thread;
slt->loop();
}
SurrogateLockerThread::SurrogateLockerThread() :
JavaThread(&_sltLoop),
_monitor(Mutex::nonleaf, "SLTMonitor"),
_buffer(empty)
{}
SurrogateLockerThread* SurrogateLockerThread::make(TRAPS) {
klassOop k =
SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_Thread(),
true, CHECK_NULL);
instanceKlassHandle klass (THREAD, k);
instanceHandle thread_oop = klass->allocate_instance_handle(CHECK_NULL);
const char thread_name[] = "Surrogate Locker Thread (CMS)";
Handle string = java_lang_String::create_from_str(thread_name, CHECK_NULL);
// Initialize thread_oop to put it into the system threadGroup
Handle thread_group (THREAD, Universe::system_thread_group());
JavaValue result(T_VOID);
JavaCalls::call_special(&result, thread_oop,
klass,
vmSymbolHandles::object_initializer_name(),
vmSymbolHandles::threadgroup_string_void_signature(),
thread_group,
string,
CHECK_NULL);
SurrogateLockerThread* res;
{
MutexLocker mu(Threads_lock);
res = new SurrogateLockerThread();
// At this point it may be possible that no osthread was created for the
// JavaThread due to lack of memory. We would have to throw an exception
// in that case. However, since this must work and we do not allow
// exceptions anyway, check and abort if this fails.
if (res == NULL || res->osthread() == NULL) {
vm_exit_during_initialization("java.lang.OutOfMemoryError",
"unable to create new native thread");
}
java_lang_Thread::set_thread(thread_oop(), res);
java_lang_Thread::set_priority(thread_oop(), NearMaxPriority);
java_lang_Thread::set_daemon(thread_oop());
res->set_threadObj(thread_oop());
Threads::add(res);
Thread::start(res);
}
os::yield(); // This seems to help with initial start-up of SLT
return res;
}
void SurrogateLockerThread::manipulatePLL(SLT_msg_type msg) {
MutexLockerEx x(&_monitor, Mutex::_no_safepoint_check_flag);
assert(_buffer == empty, "Should be empty");
assert(msg != empty, "empty message");
_buffer = msg;
while (_buffer != empty) {
_monitor.notify();
_monitor.wait(Mutex::_no_safepoint_check_flag);
}
}
// ======= Surrogate Locker Thread =============
void SurrogateLockerThread::loop() {
BasicLock pll_basic_lock;
SLT_msg_type msg;
debug_only(unsigned int owned = 0;)
while (/* !isTerminated() */ 1) {
{
MutexLocker x(&_monitor);
// Since we are a JavaThread, we can't be here at a safepoint.
assert(!SafepointSynchronize::is_at_safepoint(),
"SLT is a JavaThread");
// wait for msg buffer to become non-empty
while (_buffer == empty) {
_monitor.notify();
_monitor.wait();
}
msg = _buffer;
}
switch(msg) {
case acquirePLL: {
instanceRefKlass::acquire_pending_list_lock(&pll_basic_lock);
debug_only(owned++;)
break;
}
case releaseAndNotifyPLL: {
assert(owned > 0, "Don't have PLL");
instanceRefKlass::release_and_notify_pending_list_lock(&pll_basic_lock);
debug_only(owned--;)
break;
}
case empty:
default: {
guarantee(false,"Unexpected message in _buffer");
break;
}
}
{
MutexLocker x(&_monitor);
// Since we are a JavaThread, we can't be here at a safepoint.
assert(!SafepointSynchronize::is_at_safepoint(),
"SLT is a JavaThread");
_buffer = empty;
_monitor.notify();
}
}
assert(!_monitor.owned_by_self(), "Should unlock before exit.");
}
// ===== STS Access From Outside CGCT =====
void ConcurrentGCThread::stsYield(const char* id) {
assert( Thread::current()->is_ConcurrentGC_thread(),
"only a conc GC thread can call this" );
_sts.yield(id);
}
bool ConcurrentGCThread::stsShouldYield() {
assert( Thread::current()->is_ConcurrentGC_thread(),
"only a conc GC thread can call this" );
return _sts.should_yield();
}
void ConcurrentGCThread::stsJoin() {
assert( Thread::current()->is_ConcurrentGC_thread(),
"only a conc GC thread can call this" );
_sts.join();
}
void ConcurrentGCThread::stsLeave() {
assert( Thread::current()->is_ConcurrentGC_thread(),
"only a conc GC thread can call this" );
_sts.leave();
}

View file

@ -0,0 +1,167 @@
/*
* Copyright 2001-2005 Sun Microsystems, Inc. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
class VoidClosure;
// A SuspendibleThreadSet is (obviously) a set of threads that can be
// suspended. A thread can join and later leave the set, and periodically
// yield. If some thread (not in the set) requests, via suspend_all, that
// the threads be suspended, then the requesting thread is blocked until
// all the threads in the set have yielded or left the set. (Threads may
// not enter the set when an attempted suspension is in progress.) The
// suspending thread later calls resume_all, allowing the suspended threads
// to continue.
class SuspendibleThreadSet {
Monitor* _m;
int _async;
bool _async_stop;
int _async_stopped;
bool _initialized;
double _suspend_all_start;
void initialize_work();
public:
SuspendibleThreadSet() : _initialized(false) {}
// Add the current thread to the set. May block if a suspension
// is in progress.
void join();
// Removes the current thread from the set.
void leave();
// Returns "true" iff an suspension is in progress.
bool should_yield() { return _async_stop; }
// Suspends the current thread if a suspension is in progress (for
// the duration of the suspension.)
void yield(const char* id);
// Return when all threads in the set are suspended.
void suspend_all();
// Allow suspended threads to resume.
void resume_all();
// Redundant initializations okay.
void initialize() {
// Double-check dirty read idiom.
if (!_initialized) initialize_work();
}
};
class ConcurrentGCThread: public NamedThread {
friend class VMStructs;
protected:
static bool _should_terminate;
static bool _has_terminated;
enum CGC_flag_type {
CGC_nil = 0x0,
CGC_dont_suspend = 0x1,
CGC_CGC_safepoint = 0x2,
CGC_VM_safepoint = 0x4
};
static int _CGC_flag;
static bool CGC_flag_is_set(int b) { return (_CGC_flag & b) != 0; }
static int set_CGC_flag(int b) { return _CGC_flag |= b; }
static int reset_CGC_flag(int b) { return _CGC_flag &= ~b; }
void stopWorldAndDo(VoidClosure* op);
// All instances share this one set.
static SuspendibleThreadSet _sts;
// Create and start the thread (setting it's priority high.)
void create_and_start();
// Do initialization steps in the thread: record stack base and size,
// init thread local storage, set JNI handle block.
void initialize_in_thread();
// Wait until Universe::is_fully_initialized();
void wait_for_universe_init();
// Record that the current thread is terminating, and will do more
// concurrent work.
void terminate();
public:
// Constructor
ConcurrentGCThread();
~ConcurrentGCThread() {} // Exists to call NamedThread destructor.
// Tester
bool is_ConcurrentGC_thread() const { return true; }
static void safepoint_synchronize();
static void safepoint_desynchronize();
// All overridings should probably do _sts::yield, but we allow
// overriding for distinguished debugging messages. Default is to do
// nothing.
virtual void yield() {}
bool should_yield() { return _sts.should_yield(); }
// they are prefixed by sts since there are already yield() and
// should_yield() (non-static) methods in this class and it was an
// easy way to differentiate them.
static void stsYield(const char* id);
static bool stsShouldYield();
static void stsJoin();
static void stsLeave();
};
// The SurrogateLockerThread is used by concurrent GC threads for
// manipulating Java monitors, in particular, currently for
// manipulating the pending_list_lock. XXX
class SurrogateLockerThread: public JavaThread {
friend class VMStructs;
public:
enum SLT_msg_type {
empty = 0, // no message
acquirePLL, // acquire pending list lock
releaseAndNotifyPLL // notify and release pending list lock
};
private:
// the following are shared with the CMSThread
SLT_msg_type _buffer; // communication buffer
Monitor _monitor; // monitor controlling buffer
BasicLock _basicLock; // used for PLL locking
public:
static SurrogateLockerThread* make(TRAPS);
SurrogateLockerThread();
bool is_hidden_from_external_view() const { return true; }
void loop(); // main method
void manipulatePLL(SLT_msg_type msg);
};

View file

@ -0,0 +1,179 @@
/*
* Copyright 2001-2007 Sun Microsystems, Inc. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
# include "incls/_precompiled.incl"
# include "incls/_gcOverheadReporter.cpp.incl"
class COReportingThread : public ConcurrentGCThread {
private:
GCOverheadReporter* _reporter;
public:
COReportingThread(GCOverheadReporter* reporter) : _reporter(reporter) {
guarantee( _reporter != NULL, "precondition" );
create_and_start();
}
virtual void run() {
initialize_in_thread();
wait_for_universe_init();
int period_ms = GCOverheadReportingPeriodMS;
while ( true ) {
os::sleep(Thread::current(), period_ms, false);
_sts.join();
double now_sec = os::elapsedTime();
_reporter->collect_and_record_conc_overhead(now_sec);
_sts.leave();
}
terminate();
}
};
GCOverheadReporter* GCOverheadReporter::_reporter = NULL;
GCOverheadReporter::GCOverheadReporter(size_t group_num,
const char* group_names[],
size_t length)
: _group_num(group_num), _prev_end_sec(0.0) {
guarantee( 0 <= group_num && group_num <= MaxGCOverheadGroupNum,
"precondition" );
_base = NEW_C_HEAP_ARRAY(GCOverheadReporterEntry, length);
_top = _base + length;
_curr = _base;
for (size_t i = 0; i < group_num; ++i) {
guarantee( group_names[i] != NULL, "precondition" );
_group_names[i] = group_names[i];
}
}
void
GCOverheadReporter::add(double start_sec, double end_sec,
double* conc_overhead,
double stw_overhead) {
assert( _curr <= _top, "invariant" );
if (_curr == _top) {
guarantee( false, "trace full" );
return;
}
_curr->_start_sec = start_sec;
_curr->_end_sec = end_sec;
for (size_t i = 0; i < _group_num; ++i) {
_curr->_conc_overhead[i] =
(conc_overhead != NULL) ? conc_overhead[i] : 0.0;
}
_curr->_stw_overhead = stw_overhead;
++_curr;
}
void
GCOverheadReporter::collect_and_record_conc_overhead(double end_sec) {
double start_sec = _prev_end_sec;
guarantee( end_sec > start_sec, "invariant" );
double conc_overhead[MaxGCOverheadGroupNum];
COTracker::totalConcOverhead(end_sec, _group_num, conc_overhead);
add_conc_overhead(start_sec, end_sec, conc_overhead);
_prev_end_sec = end_sec;
}
void
GCOverheadReporter::record_stw_start(double start_sec) {
guarantee( start_sec > _prev_end_sec, "invariant" );
collect_and_record_conc_overhead(start_sec);
}
void
GCOverheadReporter::record_stw_end(double end_sec) {
double start_sec = _prev_end_sec;
COTracker::updateAllForSTW(start_sec, end_sec);
add_stw_overhead(start_sec, end_sec, 1.0);
_prev_end_sec = end_sec;
}
void
GCOverheadReporter::print() const {
tty->print_cr("");
tty->print_cr("GC Overhead (%d entries)", _curr - _base);
tty->print_cr("");
GCOverheadReporterEntry* curr = _base;
while (curr < _curr) {
double total = curr->_stw_overhead;
for (size_t i = 0; i < _group_num; ++i)
total += curr->_conc_overhead[i];
tty->print("OVERHEAD %12.8lf %12.8lf ",
curr->_start_sec, curr->_end_sec);
for (size_t i = 0; i < _group_num; ++i)
tty->print("%s %12.8lf ", _group_names[i], curr->_conc_overhead[i]);
tty->print_cr("STW %12.8lf TOT %12.8lf", curr->_stw_overhead, total);
++curr;
}
tty->print_cr("");
}
// statics
void
GCOverheadReporter::initGCOverheadReporter(size_t group_num,
const char* group_names[]) {
guarantee( _reporter == NULL, "should only be called once" );
guarantee( 0 <= group_num && group_num <= MaxGCOverheadGroupNum,
"precondition" );
guarantee( group_names != NULL, "pre-condition" );
if (GCOverheadReporting) {
_reporter = new GCOverheadReporter(group_num, group_names);
new COReportingThread(_reporter);
}
}
void
GCOverheadReporter::recordSTWStart(double start_sec) {
if (_reporter != NULL)
_reporter->record_stw_start(start_sec);
}
void
GCOverheadReporter::recordSTWEnd(double end_sec) {
if (_reporter != NULL)
_reporter->record_stw_end(end_sec);
}
void
GCOverheadReporter::printGCOverhead() {
if (_reporter != NULL)
_reporter->print();
}

View file

@ -0,0 +1,141 @@
/*
* Copyright 2001-2007 Sun Microsystems, Inc. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
// Keeps track of the GC overhead (both concurrent and STW). It stores
// it in a large array and then prints it to tty at the end of the
// execution.
// See coTracker.hpp for the explanation on what groups are.
// Let's set a maximum number of concurrent overhead groups, to
// statically allocate any arrays we need and not to have to
// malloc/free them. This is just a bit more convenient.
enum {
MaxGCOverheadGroupNum = 4
};
typedef struct {
double _start_sec;
double _end_sec;
double _conc_overhead[MaxGCOverheadGroupNum];
double _stw_overhead;
} GCOverheadReporterEntry;
class GCOverheadReporter {
friend class COReportingThread;
private:
enum PrivateConstants {
DefaultReporterLength = 128 * 1024
};
// Reference to the single instance of this class.
static GCOverheadReporter* _reporter;
// These three references point to the array that contains the GC
// overhead entries (_base is the base of the array, _top is the
// address passed the last entry of the array, _curr is the next
// entry to be used).
GCOverheadReporterEntry* _base;
GCOverheadReporterEntry* _top;
GCOverheadReporterEntry* _curr;
// The number of concurrent overhead groups.
size_t _group_num;
// The wall-clock time of the end of the last recorded period of GC
// overhead.
double _prev_end_sec;
// Names for the concurrent overhead groups.
const char* _group_names[MaxGCOverheadGroupNum];
// Add a new entry to the large array. conc_overhead being NULL is
// equivalent to an array full of 0.0s. conc_overhead should have a
// length of at least _group_num.
void add(double start_sec, double end_sec,
double* conc_overhead,
double stw_overhead);
// Add an entry that represents concurrent GC overhead.
// conc_overhead must be at least of length _group_num.
// conc_overhead being NULL is equivalent to an array full of 0.0s.
void add_conc_overhead(double start_sec, double end_sec,
double* conc_overhead) {
add(start_sec, end_sec, conc_overhead, 0.0);
}
// Add an entry that represents STW GC overhead.
void add_stw_overhead(double start_sec, double end_sec,
double stw_overhead) {
add(start_sec, end_sec, NULL, stw_overhead);
}
// It records the start of a STW pause (i.e. it records the
// concurrent overhead up to that point)
void record_stw_start(double start_sec);
// It records the end of a STW pause (i.e. it records the overhead
// associated with the pause and adjusts all the trackers to reflect
// the pause)
void record_stw_end(double end_sec);
// It queries all the trackers of their concurrent overhead and
// records it.
void collect_and_record_conc_overhead(double end_sec);
// It prints the contents of the GC overhead array
void print() const;
// Constructor. The same preconditions for group_num and group_names
// from initGCOverheadReporter apply here too.
GCOverheadReporter(size_t group_num,
const char* group_names[],
size_t length = DefaultReporterLength);
public:
// statics
// It initialises the GCOverheadReporter and launches the concurrent
// overhead reporting thread. Both actions happen only if the
// GCOverheadReporting parameter is set. The length of the
// group_names array should be >= group_num and group_num should be
// <= MaxGCOverheadGroupNum. Entries group_namnes[0..group_num-1]
// should not be NULL.
static void initGCOverheadReporter(size_t group_num,
const char* group_names[]);
// The following three are provided for convenience and they are
// wrappers around record_stw_start(start_sec), record_stw_end(end_sec),
// and print(). Each of these checks whether GC overhead reporting
// is on (i.e. _reporter != NULL) and, if it is, calls the
// corresponding method. Saves from repeating this pattern again and
// again from the places where they need to be called.
static void recordSTWStart(double start_sec);
static void recordSTWEnd(double end_sec);
static void printGCOverhead();
};

View file

@ -74,6 +74,7 @@ bool VM_GC_Operation::doit_prologue() {
// If the GC count has changed someone beat us to the collection
// Get the Heap_lock after the pending_list_lock.
Heap_lock->lock();
// Check invocations
if (skip_operation()) {
// skip collection
@ -82,6 +83,8 @@ bool VM_GC_Operation::doit_prologue() {
_prologue_succeeded = false;
} else {
_prologue_succeeded = true;
SharedHeap* sh = SharedHeap::heap();
if (sh != NULL) sh->_thread_holds_heap_lock_for_gc = true;
}
return _prologue_succeeded;
}
@ -90,6 +93,8 @@ bool VM_GC_Operation::doit_prologue() {
void VM_GC_Operation::doit_epilogue() {
assert(Thread::current()->is_Java_thread(), "just checking");
// Release the Heap_lock first.
SharedHeap* sh = SharedHeap::heap();
if (sh != NULL) sh->_thread_holds_heap_lock_for_gc = false;
Heap_lock->unlock();
release_and_notify_pending_list_lock();
}
@ -148,12 +153,27 @@ void VM_GenCollectFull::doit() {
void VM_GenCollectForPermanentAllocation::doit() {
JvmtiGCForAllocationMarker jgcm;
notify_gc_begin(true);
GenCollectedHeap* gch = GenCollectedHeap::heap();
GCCauseSetter gccs(gch, _gc_cause);
gch->do_full_collection(gch->must_clear_all_soft_refs(),
gch->n_gens() - 1);
_res = gch->perm_gen()->allocate(_size, false);
assert(gch->is_in_reserved_or_null(_res), "result not in heap");
SharedHeap* heap = (SharedHeap*)Universe::heap();
GCCauseSetter gccs(heap, _gc_cause);
switch (heap->kind()) {
case (CollectedHeap::GenCollectedHeap): {
GenCollectedHeap* gch = (GenCollectedHeap*)heap;
gch->do_full_collection(gch->must_clear_all_soft_refs(),
gch->n_gens() - 1);
break;
}
#ifndef SERIALGC
case (CollectedHeap::G1CollectedHeap): {
G1CollectedHeap* g1h = (G1CollectedHeap*)heap;
g1h->do_full_collection(_gc_cause == GCCause::_last_ditch_collection);
break;
}
#endif // SERIALGC
default:
ShouldNotReachHere();
}
_res = heap->perm_gen()->allocate(_size, false);
assert(heap->is_in_reserved_or_null(_res), "result not in heap");
if (_res == NULL && GC_locker::is_active_and_needs_gc()) {
set_gc_locked();
}