mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-10 18:38:27 +00:00
Merge
This commit is contained in:
commit
7daca4fc60
@ -6092,7 +6092,11 @@ void CMSCollector::sweep(bool asynch) {
|
||||
_inter_sweep_timer.reset();
|
||||
_inter_sweep_timer.start();
|
||||
|
||||
update_time_of_last_gc(os::javaTimeMillis());
|
||||
// We need to use a monotonically non-deccreasing time in ms
|
||||
// or we will see time-warp warnings and os::javaTimeMillis()
|
||||
// does not guarantee monotonicity.
|
||||
jlong now = os::javaTimeNanos() / NANOSECS_PER_MILLISEC;
|
||||
update_time_of_last_gc(now);
|
||||
|
||||
// NOTE on abstract state transitions:
|
||||
// Mutators allocate-live and/or mark the mod-union table dirty
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2012, 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
|
||||
@ -48,6 +48,8 @@ void CSetChooserCache::clear() {
|
||||
|
||||
#ifndef PRODUCT
|
||||
bool CSetChooserCache::verify() {
|
||||
guarantee(false, "CSetChooserCache::verify(): don't call this any more");
|
||||
|
||||
int index = _first;
|
||||
HeapRegion *prev = NULL;
|
||||
for (int i = 0; i < _occupancy; ++i) {
|
||||
@ -75,6 +77,8 @@ bool CSetChooserCache::verify() {
|
||||
#endif // PRODUCT
|
||||
|
||||
void CSetChooserCache::insert(HeapRegion *hr) {
|
||||
guarantee(false, "CSetChooserCache::insert(): don't call this any more");
|
||||
|
||||
assert(!is_full(), "cache should not be empty");
|
||||
hr->calc_gc_efficiency();
|
||||
|
||||
@ -104,6 +108,9 @@ void CSetChooserCache::insert(HeapRegion *hr) {
|
||||
}
|
||||
|
||||
HeapRegion *CSetChooserCache::remove_first() {
|
||||
guarantee(false, "CSetChooserCache::remove_first(): "
|
||||
"don't call this any more");
|
||||
|
||||
if (_occupancy > 0) {
|
||||
assert(_cache[_first] != NULL, "cache should have at least one region");
|
||||
HeapRegion *ret = _cache[_first];
|
||||
@ -118,16 +125,35 @@ HeapRegion *CSetChooserCache::remove_first() {
|
||||
}
|
||||
}
|
||||
|
||||
static inline int orderRegions(HeapRegion* hr1, HeapRegion* hr2) {
|
||||
// Even though we don't use the GC efficiency in our heuristics as
|
||||
// much as we used to, we still order according to GC efficiency. This
|
||||
// will cause regions with a lot of live objects and large RSets to
|
||||
// end up at the end of the array. Given that we might skip collecting
|
||||
// the last few old regions, if after a few mixed GCs the remaining
|
||||
// have reclaimable bytes under a certain threshold, the hope is that
|
||||
// the ones we'll skip are ones with both large RSets and a lot of
|
||||
// live objects, not the ones with just a lot of live objects if we
|
||||
// ordered according to the amount of reclaimable bytes per region.
|
||||
static int orderRegions(HeapRegion* hr1, HeapRegion* hr2) {
|
||||
if (hr1 == NULL) {
|
||||
if (hr2 == NULL) return 0;
|
||||
else return 1;
|
||||
if (hr2 == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else if (hr2 == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (hr2->gc_efficiency() < hr1->gc_efficiency()) return -1;
|
||||
else if (hr1->gc_efficiency() < hr2->gc_efficiency()) return 1;
|
||||
else return 0;
|
||||
|
||||
double gc_eff1 = hr1->gc_efficiency();
|
||||
double gc_eff2 = hr2->gc_efficiency();
|
||||
if (gc_eff1 > gc_eff2) {
|
||||
return -1;
|
||||
} if (gc_eff1 < gc_eff2) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int orderRegions(HeapRegion** hr1p, HeapRegion** hr2p) {
|
||||
@ -151,51 +177,61 @@ CollectionSetChooser::CollectionSetChooser() :
|
||||
//
|
||||
_markedRegions((ResourceObj::set_allocation_type((address)&_markedRegions,
|
||||
ResourceObj::C_HEAP),
|
||||
100),
|
||||
true),
|
||||
_curMarkedIndex(0),
|
||||
_numMarkedRegions(0),
|
||||
_unmarked_age_1_returned_as_new(false),
|
||||
_first_par_unreserved_idx(0)
|
||||
{}
|
||||
|
||||
|
||||
100), true /* C_Heap */),
|
||||
_curr_index(0), _length(0),
|
||||
_regionLiveThresholdBytes(0), _remainingReclaimableBytes(0),
|
||||
_first_par_unreserved_idx(0) {
|
||||
_regionLiveThresholdBytes =
|
||||
HeapRegion::GrainBytes * (size_t) G1OldCSetRegionLiveThresholdPercent / 100;
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
bool CollectionSetChooser::verify() {
|
||||
guarantee(_length >= 0, err_msg("_length: %d", _length));
|
||||
guarantee(0 <= _curr_index && _curr_index <= _length,
|
||||
err_msg("_curr_index: %d _length: %d", _curr_index, _length));
|
||||
int index = 0;
|
||||
guarantee(_curMarkedIndex <= _numMarkedRegions,
|
||||
"_curMarkedIndex should be within bounds");
|
||||
while (index < _curMarkedIndex) {
|
||||
guarantee(_markedRegions.at(index++) == NULL,
|
||||
"all entries before _curMarkedIndex should be NULL");
|
||||
size_t sum_of_reclaimable_bytes = 0;
|
||||
while (index < _curr_index) {
|
||||
guarantee(_markedRegions.at(index) == NULL,
|
||||
"all entries before _curr_index should be NULL");
|
||||
index += 1;
|
||||
}
|
||||
HeapRegion *prev = NULL;
|
||||
while (index < _numMarkedRegions) {
|
||||
while (index < _length) {
|
||||
HeapRegion *curr = _markedRegions.at(index++);
|
||||
guarantee(curr != NULL, "Regions in _markedRegions array cannot be NULL");
|
||||
int si = curr->sort_index();
|
||||
guarantee(!curr->is_young(), "should not be young!");
|
||||
guarantee(!curr->isHumongous(), "should not be humongous!");
|
||||
guarantee(si > -1 && si == (index-1), "sort index invariant");
|
||||
if (prev != NULL) {
|
||||
guarantee(orderRegions(prev, curr) != 1, "regions should be sorted");
|
||||
guarantee(orderRegions(prev, curr) != 1,
|
||||
err_msg("GC eff prev: %1.4f GC eff curr: %1.4f",
|
||||
prev->gc_efficiency(), curr->gc_efficiency()));
|
||||
}
|
||||
sum_of_reclaimable_bytes += curr->reclaimable_bytes();
|
||||
prev = curr;
|
||||
}
|
||||
return _cache.verify();
|
||||
guarantee(sum_of_reclaimable_bytes == _remainingReclaimableBytes,
|
||||
err_msg("reclaimable bytes inconsistent, "
|
||||
"remaining: "SIZE_FORMAT" sum: "SIZE_FORMAT,
|
||||
_remainingReclaimableBytes, sum_of_reclaimable_bytes));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
CollectionSetChooser::fillCache() {
|
||||
while (!_cache.is_full() && (_curMarkedIndex < _numMarkedRegions)) {
|
||||
HeapRegion* hr = _markedRegions.at(_curMarkedIndex);
|
||||
void CollectionSetChooser::fillCache() {
|
||||
guarantee(false, "fillCache: don't call this any more");
|
||||
|
||||
while (!_cache.is_full() && (_curr_index < _length)) {
|
||||
HeapRegion* hr = _markedRegions.at(_curr_index);
|
||||
assert(hr != NULL,
|
||||
err_msg("Unexpected NULL hr in _markedRegions at index %d",
|
||||
_curMarkedIndex));
|
||||
_curMarkedIndex += 1;
|
||||
_curr_index));
|
||||
_curr_index += 1;
|
||||
assert(!hr->is_young(), "should not be young!");
|
||||
assert(hr->sort_index() == _curMarkedIndex-1, "sort_index invariant");
|
||||
assert(hr->sort_index() == _curr_index-1, "sort_index invariant");
|
||||
_markedRegions.at_put(hr->sort_index(), NULL);
|
||||
_cache.insert(hr);
|
||||
assert(!_cache.is_empty(), "cache should not be empty");
|
||||
@ -203,9 +239,7 @@ CollectionSetChooser::fillCache() {
|
||||
assert(verify(), "cache should be consistent");
|
||||
}
|
||||
|
||||
void
|
||||
CollectionSetChooser::sortMarkedHeapRegions() {
|
||||
guarantee(_cache.is_empty(), "cache should be empty");
|
||||
void CollectionSetChooser::sortMarkedHeapRegions() {
|
||||
// First trim any unused portion of the top in the parallel case.
|
||||
if (_first_par_unreserved_idx > 0) {
|
||||
if (G1PrintParCleanupStats) {
|
||||
@ -217,43 +251,78 @@ CollectionSetChooser::sortMarkedHeapRegions() {
|
||||
_markedRegions.trunc_to(_first_par_unreserved_idx);
|
||||
}
|
||||
_markedRegions.sort(orderRegions);
|
||||
assert(_numMarkedRegions <= _markedRegions.length(), "Requirement");
|
||||
assert(_numMarkedRegions == 0
|
||||
|| _markedRegions.at(_numMarkedRegions-1) != NULL,
|
||||
"Testing _numMarkedRegions");
|
||||
assert(_numMarkedRegions == _markedRegions.length()
|
||||
|| _markedRegions.at(_numMarkedRegions) == NULL,
|
||||
"Testing _numMarkedRegions");
|
||||
assert(_length <= _markedRegions.length(), "Requirement");
|
||||
assert(_length == 0 || _markedRegions.at(_length - 1) != NULL,
|
||||
"Testing _length");
|
||||
assert(_length == _markedRegions.length() ||
|
||||
_markedRegions.at(_length) == NULL, "Testing _length");
|
||||
if (G1PrintParCleanupStats) {
|
||||
gclog_or_tty->print_cr(" Sorted %d marked regions.", _numMarkedRegions);
|
||||
gclog_or_tty->print_cr(" Sorted %d marked regions.", _length);
|
||||
}
|
||||
for (int i = 0; i < _numMarkedRegions; i++) {
|
||||
for (int i = 0; i < _length; i++) {
|
||||
assert(_markedRegions.at(i) != NULL, "Should be true by sorting!");
|
||||
_markedRegions.at(i)->set_sort_index(i);
|
||||
}
|
||||
if (G1PrintRegionLivenessInfo) {
|
||||
G1PrintRegionLivenessInfoClosure cl(gclog_or_tty, "Post-Sorting");
|
||||
for (int i = 0; i < _numMarkedRegions; ++i) {
|
||||
for (int i = 0; i < _length; ++i) {
|
||||
HeapRegion* r = _markedRegions.at(i);
|
||||
cl.doHeapRegion(r);
|
||||
}
|
||||
}
|
||||
assert(verify(), "should now be sorted");
|
||||
assert(verify(), "CSet chooser verification");
|
||||
}
|
||||
|
||||
void
|
||||
CollectionSetChooser::addMarkedHeapRegion(HeapRegion* hr) {
|
||||
size_t CollectionSetChooser::calcMinOldCSetLength() {
|
||||
// The min old CSet region bound is based on the maximum desired
|
||||
// number of mixed GCs after a cycle. I.e., even if some old regions
|
||||
// look expensive, we should add them to the CSet anyway to make
|
||||
// sure we go through the available old regions in no more than the
|
||||
// maximum desired number of mixed GCs.
|
||||
//
|
||||
// The calculation is based on the number of marked regions we added
|
||||
// to the CSet chooser in the first place, not how many remain, so
|
||||
// that the result is the same during all mixed GCs that follow a cycle.
|
||||
|
||||
const size_t region_num = (size_t) _length;
|
||||
const size_t gc_num = (size_t) G1MaxMixedGCNum;
|
||||
size_t result = region_num / gc_num;
|
||||
// emulate ceiling
|
||||
if (result * gc_num < region_num) {
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t CollectionSetChooser::calcMaxOldCSetLength() {
|
||||
// The max old CSet region bound is based on the threshold expressed
|
||||
// as a percentage of the heap size. I.e., it should bound the
|
||||
// number of old regions added to the CSet irrespective of how many
|
||||
// of them are available.
|
||||
|
||||
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||
const size_t region_num = g1h->n_regions();
|
||||
const size_t perc = (size_t) G1OldCSetRegionThresholdPercent;
|
||||
size_t result = region_num * perc / 100;
|
||||
// emulate ceiling
|
||||
if (100 * result < region_num * perc) {
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CollectionSetChooser::addMarkedHeapRegion(HeapRegion* hr) {
|
||||
assert(!hr->isHumongous(),
|
||||
"Humongous regions shouldn't be added to the collection set");
|
||||
assert(!hr->is_young(), "should not be young!");
|
||||
_markedRegions.append(hr);
|
||||
_numMarkedRegions++;
|
||||
_length++;
|
||||
_remainingReclaimableBytes += hr->reclaimable_bytes();
|
||||
hr->calc_gc_efficiency();
|
||||
}
|
||||
|
||||
void
|
||||
CollectionSetChooser::
|
||||
prepareForAddMarkedHeapRegionsPar(size_t n_regions, size_t chunkSize) {
|
||||
void CollectionSetChooser::prepareForAddMarkedHeapRegionsPar(size_t n_regions,
|
||||
size_t chunkSize) {
|
||||
_first_par_unreserved_idx = 0;
|
||||
int n_threads = ParallelGCThreads;
|
||||
if (UseDynamicNumberOfGCThreads) {
|
||||
@ -274,8 +343,7 @@ prepareForAddMarkedHeapRegionsPar(size_t n_regions, size_t chunkSize) {
|
||||
_markedRegions.at_put_grow((int)(aligned_n_regions + max_waste - 1), NULL);
|
||||
}
|
||||
|
||||
jint
|
||||
CollectionSetChooser::getParMarkedHeapRegionChunk(jint n_regions) {
|
||||
jint CollectionSetChooser::getParMarkedHeapRegionChunk(jint n_regions) {
|
||||
// Don't do this assert because this can be called at a point
|
||||
// where the loop up stream will not execute again but might
|
||||
// try to claim more chunks (loop test has not been done yet).
|
||||
@ -287,83 +355,37 @@ CollectionSetChooser::getParMarkedHeapRegionChunk(jint n_regions) {
|
||||
return res - n_regions;
|
||||
}
|
||||
|
||||
void
|
||||
CollectionSetChooser::setMarkedHeapRegion(jint index, HeapRegion* hr) {
|
||||
void CollectionSetChooser::setMarkedHeapRegion(jint index, HeapRegion* hr) {
|
||||
assert(_markedRegions.at(index) == NULL, "precondition");
|
||||
assert(!hr->is_young(), "should not be young!");
|
||||
_markedRegions.at_put(index, hr);
|
||||
hr->calc_gc_efficiency();
|
||||
}
|
||||
|
||||
void
|
||||
CollectionSetChooser::incNumMarkedHeapRegions(jint inc_by) {
|
||||
(void)Atomic::add(inc_by, &_numMarkedRegions);
|
||||
void CollectionSetChooser::updateTotals(jint region_num,
|
||||
size_t reclaimable_bytes) {
|
||||
// Only take the lock if we actually need to update the totals.
|
||||
if (region_num > 0) {
|
||||
assert(reclaimable_bytes > 0, "invariant");
|
||||
// We could have just used atomics instead of taking the
|
||||
// lock. However, we currently don't have an atomic add for size_t.
|
||||
MutexLockerEx x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag);
|
||||
_length += (int) region_num;
|
||||
_remainingReclaimableBytes += reclaimable_bytes;
|
||||
} else {
|
||||
assert(reclaimable_bytes == 0, "invariant");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CollectionSetChooser::clearMarkedHeapRegions(){
|
||||
void CollectionSetChooser::clearMarkedHeapRegions() {
|
||||
for (int i = 0; i < _markedRegions.length(); i++) {
|
||||
HeapRegion* r = _markedRegions.at(i);
|
||||
if (r != NULL) r->set_sort_index(-1);
|
||||
HeapRegion* r = _markedRegions.at(i);
|
||||
if (r != NULL) {
|
||||
r->set_sort_index(-1);
|
||||
}
|
||||
}
|
||||
_markedRegions.clear();
|
||||
_curMarkedIndex = 0;
|
||||
_numMarkedRegions = 0;
|
||||
_cache.clear();
|
||||
_curr_index = 0;
|
||||
_length = 0;
|
||||
_remainingReclaimableBytes = 0;
|
||||
};
|
||||
|
||||
void
|
||||
CollectionSetChooser::updateAfterFullCollection() {
|
||||
clearMarkedHeapRegions();
|
||||
}
|
||||
|
||||
// if time_remaining < 0.0, then this method should try to return
|
||||
// a region, whether it fits within the remaining time or not
|
||||
HeapRegion*
|
||||
CollectionSetChooser::getNextMarkedRegion(double time_remaining,
|
||||
double avg_prediction) {
|
||||
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||
G1CollectorPolicy* g1p = g1h->g1_policy();
|
||||
fillCache();
|
||||
if (_cache.is_empty()) {
|
||||
assert(_curMarkedIndex == _numMarkedRegions,
|
||||
"if cache is empty, list should also be empty");
|
||||
ergo_verbose0(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("cache is empty"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HeapRegion *hr = _cache.get_first();
|
||||
assert(hr != NULL, "if cache not empty, first entry should be non-null");
|
||||
double predicted_time = g1h->predict_region_elapsed_time_ms(hr, false);
|
||||
|
||||
if (g1p->adaptive_young_list_length()) {
|
||||
if (time_remaining - predicted_time < 0.0) {
|
||||
g1h->check_if_region_is_too_expensive(predicted_time);
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("predicted old region time higher than remaining time")
|
||||
ergo_format_ms("predicted old region time")
|
||||
ergo_format_ms("remaining time"),
|
||||
predicted_time, time_remaining);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
double threshold = 2.0 * avg_prediction;
|
||||
if (predicted_time > threshold) {
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("predicted old region time higher than threshold")
|
||||
ergo_format_ms("predicted old region time")
|
||||
ergo_format_ms("threshold"),
|
||||
predicted_time, threshold);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HeapRegion *hr2 = _cache.remove_first();
|
||||
assert(hr == hr2, "cache contents should not have changed");
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2012, 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
|
||||
@ -28,28 +28,6 @@
|
||||
#include "gc_implementation/g1/heapRegion.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
// We need to sort heap regions by collection desirability.
|
||||
// This sorting is currently done in two "stages". An initial sort is
|
||||
// done following a cleanup pause as soon as all of the marked but
|
||||
// non-empty regions have been identified and the completely empty
|
||||
// ones reclaimed.
|
||||
// This gives us a global sort on a GC efficiency metric
|
||||
// based on predictive data available at that time. However,
|
||||
// any of these regions that are collected will only be collected
|
||||
// during a future GC pause, by which time it is possible that newer
|
||||
// data might allow us to revise and/or refine the earlier
|
||||
// pause predictions, leading to changes in expected gc efficiency
|
||||
// order. To somewhat mitigate this obsolescence, more so in the
|
||||
// case of regions towards the end of the list, which will be
|
||||
// picked later, these pre-sorted regions from the _markedRegions
|
||||
// array are not used as is, but a small prefix thereof is
|
||||
// insertion-sorted again into a small cache, based on more
|
||||
// recent remembered set information. Regions are then drawn
|
||||
// from this cache to construct the collection set at each
|
||||
// incremental GC.
|
||||
// This scheme and/or its implementation may be subject to
|
||||
// revision in the future.
|
||||
|
||||
class CSetChooserCache VALUE_OBJ_CLASS_SPEC {
|
||||
private:
|
||||
enum {
|
||||
@ -103,24 +81,82 @@ public:
|
||||
class CollectionSetChooser: public CHeapObj {
|
||||
|
||||
GrowableArray<HeapRegion*> _markedRegions;
|
||||
int _curMarkedIndex;
|
||||
int _numMarkedRegions;
|
||||
|
||||
// The index of the next candidate old region to be considered for
|
||||
// addition to the CSet.
|
||||
int _curr_index;
|
||||
|
||||
// The number of candidate old regions added to the CSet chooser.
|
||||
int _length;
|
||||
|
||||
CSetChooserCache _cache;
|
||||
|
||||
// True iff last collection pause ran of out new "age 0" regions, and
|
||||
// returned an "age 1" region.
|
||||
bool _unmarked_age_1_returned_as_new;
|
||||
|
||||
jint _first_par_unreserved_idx;
|
||||
|
||||
// If a region has more live bytes than this threshold, it will not
|
||||
// be added to the CSet chooser and will not be a candidate for
|
||||
// collection.
|
||||
size_t _regionLiveThresholdBytes;
|
||||
|
||||
// The sum of reclaimable bytes over all the regions in the CSet chooser.
|
||||
size_t _remainingReclaimableBytes;
|
||||
|
||||
public:
|
||||
|
||||
HeapRegion* getNextMarkedRegion(double time_so_far, double avg_prediction);
|
||||
// Return the current candidate region to be considered for
|
||||
// collection without removing it from the CSet chooser.
|
||||
HeapRegion* peek() {
|
||||
HeapRegion* res = NULL;
|
||||
if (_curr_index < _length) {
|
||||
res = _markedRegions.at(_curr_index);
|
||||
assert(res != NULL,
|
||||
err_msg("Unexpected NULL hr in _markedRegions at index %d",
|
||||
_curr_index));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Remove the given region from the CSet chooser and move to the
|
||||
// next one. The given region should be the current candidate region
|
||||
// in the CSet chooser.
|
||||
void remove_and_move_to_next(HeapRegion* hr) {
|
||||
assert(hr != NULL, "pre-condition");
|
||||
assert(_curr_index < _length, "pre-condition");
|
||||
assert(_markedRegions.at(_curr_index) == hr, "pre-condition");
|
||||
hr->set_sort_index(-1);
|
||||
_markedRegions.at_put(_curr_index, NULL);
|
||||
assert(hr->reclaimable_bytes() <= _remainingReclaimableBytes,
|
||||
err_msg("remaining reclaimable bytes inconsistent "
|
||||
"from region: "SIZE_FORMAT" remaining: "SIZE_FORMAT,
|
||||
hr->reclaimable_bytes(), _remainingReclaimableBytes));
|
||||
_remainingReclaimableBytes -= hr->reclaimable_bytes();
|
||||
_curr_index += 1;
|
||||
}
|
||||
|
||||
CollectionSetChooser();
|
||||
|
||||
void sortMarkedHeapRegions();
|
||||
void fillCache();
|
||||
|
||||
// Determine whether to add the given region to the CSet chooser or
|
||||
// not. Currently, we skip humongous regions (we never add them to
|
||||
// the CSet, we only reclaim them during cleanup) and regions whose
|
||||
// live bytes are over the threshold.
|
||||
bool shouldAdd(HeapRegion* hr) {
|
||||
assert(hr->is_marked(), "pre-condition");
|
||||
assert(!hr->is_young(), "should never consider young regions");
|
||||
return !hr->isHumongous() &&
|
||||
hr->live_bytes() < _regionLiveThresholdBytes;
|
||||
}
|
||||
|
||||
// Calculate the minimum number of old regions we'll add to the CSet
|
||||
// during a mixed GC.
|
||||
size_t calcMinOldCSetLength();
|
||||
|
||||
// Calculate the maximum number of old regions we'll add to the CSet
|
||||
// during a mixed GC.
|
||||
size_t calcMaxOldCSetLength();
|
||||
|
||||
// Serial version.
|
||||
void addMarkedHeapRegion(HeapRegion *hr);
|
||||
|
||||
// Must be called before calls to getParMarkedHeapRegionChunk.
|
||||
@ -133,14 +169,21 @@ public:
|
||||
// Set the marked array entry at index to hr. Careful to claim the index
|
||||
// first if in parallel.
|
||||
void setMarkedHeapRegion(jint index, HeapRegion* hr);
|
||||
// Atomically increment the number of claimed regions by "inc_by".
|
||||
void incNumMarkedHeapRegions(jint inc_by);
|
||||
// Atomically increment the number of added regions by region_num
|
||||
// and the amount of reclaimable bytes by reclaimable_bytes.
|
||||
void updateTotals(jint region_num, size_t reclaimable_bytes);
|
||||
|
||||
void clearMarkedHeapRegions();
|
||||
|
||||
void updateAfterFullCollection();
|
||||
// Return the number of candidate regions that remain to be collected.
|
||||
size_t remainingRegions() { return _length - _curr_index; }
|
||||
|
||||
bool unmarked_age_1_returned_as_new() { return _unmarked_age_1_returned_as_new; }
|
||||
// Determine whether the CSet chooser has more candidate regions or not.
|
||||
bool isEmpty() { return remainingRegions() == 0; }
|
||||
|
||||
// Return the reclaimable bytes that remain to be collected on
|
||||
// all the candidate regions in the CSet chooser.
|
||||
size_t remainingReclaimableBytes () { return _remainingReclaimableBytes; }
|
||||
|
||||
// Returns true if the used portion of "_markedRegions" is properly
|
||||
// sorted, otherwise asserts false.
|
||||
@ -148,9 +191,17 @@ public:
|
||||
bool verify(void);
|
||||
bool regionProperlyOrdered(HeapRegion* r) {
|
||||
int si = r->sort_index();
|
||||
return (si == -1) ||
|
||||
(si > -1 && _markedRegions.at(si) == r) ||
|
||||
(si < -1 && _cache.region_in_cache(r));
|
||||
if (si > -1) {
|
||||
guarantee(_curr_index <= si && si < _length,
|
||||
err_msg("curr: %d sort index: %d: length: %d",
|
||||
_curr_index, si, _length));
|
||||
guarantee(_markedRegions.at(si) == r,
|
||||
err_msg("sort index: %d at: "PTR_FORMAT" r: "PTR_FORMAT,
|
||||
si, _markedRegions.at(si), r));
|
||||
} else {
|
||||
guarantee(si == -1, err_msg("sort index: %d", si));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -958,7 +958,7 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size,
|
||||
should_try_gc = false;
|
||||
} else {
|
||||
// Read the GC count while still holding the Heap_lock.
|
||||
gc_count_before = SharedHeap::heap()->total_collections();
|
||||
gc_count_before = total_collections();
|
||||
should_try_gc = true;
|
||||
}
|
||||
}
|
||||
@ -976,7 +976,7 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size,
|
||||
// failed to allocate. No point in trying to allocate
|
||||
// further. We'll just return NULL.
|
||||
MutexLockerEx x(Heap_lock);
|
||||
*gc_count_before_ret = SharedHeap::heap()->total_collections();
|
||||
*gc_count_before_ret = total_collections();
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
@ -1031,7 +1031,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size,
|
||||
// the check before we do the actual allocation. The reason for doing it
|
||||
// before the allocation is that we avoid having to keep track of the newly
|
||||
// allocated memory while we do a GC.
|
||||
if (g1_policy()->need_to_start_conc_mark("concurrent humongous allocation", word_size)) {
|
||||
if (g1_policy()->need_to_start_conc_mark("concurrent humongous allocation",
|
||||
word_size)) {
|
||||
collect(GCCause::_g1_humongous_allocation);
|
||||
}
|
||||
|
||||
@ -1059,7 +1060,7 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size,
|
||||
should_try_gc = false;
|
||||
} else {
|
||||
// Read the GC count while still holding the Heap_lock.
|
||||
gc_count_before = SharedHeap::heap()->total_collections();
|
||||
gc_count_before = total_collections();
|
||||
should_try_gc = true;
|
||||
}
|
||||
}
|
||||
@ -1081,7 +1082,7 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size,
|
||||
// failed to allocate. No point in trying to allocate
|
||||
// further. We'll just return NULL.
|
||||
MutexLockerEx x(Heap_lock);
|
||||
*gc_count_before_ret = SharedHeap::heap()->total_collections();
|
||||
*gc_count_before_ret = total_collections();
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
@ -2311,10 +2312,12 @@ size_t G1CollectedHeap::unsafe_max_alloc() {
|
||||
}
|
||||
|
||||
bool G1CollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) {
|
||||
return
|
||||
((cause == GCCause::_gc_locker && GCLockerInvokesConcurrent) ||
|
||||
(cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent) ||
|
||||
cause == GCCause::_g1_humongous_allocation);
|
||||
switch (cause) {
|
||||
case GCCause::_gc_locker: return GCLockerInvokesConcurrent;
|
||||
case GCCause::_java_lang_system_gc: return ExplicitGCInvokesConcurrent;
|
||||
case GCCause::_g1_humongous_allocation: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
@ -2408,47 +2411,66 @@ void G1CollectedHeap::collect_as_vm_thread(GCCause::Cause cause) {
|
||||
}
|
||||
|
||||
void G1CollectedHeap::collect(GCCause::Cause cause) {
|
||||
// The caller doesn't have the Heap_lock
|
||||
assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock");
|
||||
assert_heap_not_locked();
|
||||
|
||||
unsigned int gc_count_before;
|
||||
unsigned int full_gc_count_before;
|
||||
{
|
||||
MutexLocker ml(Heap_lock);
|
||||
bool retry_gc;
|
||||
|
||||
// Read the GC count while holding the Heap_lock
|
||||
gc_count_before = SharedHeap::heap()->total_collections();
|
||||
full_gc_count_before = SharedHeap::heap()->total_full_collections();
|
||||
}
|
||||
do {
|
||||
retry_gc = false;
|
||||
|
||||
if (should_do_concurrent_full_gc(cause)) {
|
||||
// Schedule an initial-mark evacuation pause that will start a
|
||||
// concurrent cycle. We're setting word_size to 0 which means that
|
||||
// we are not requesting a post-GC allocation.
|
||||
VM_G1IncCollectionPause op(gc_count_before,
|
||||
0, /* word_size */
|
||||
true, /* should_initiate_conc_mark */
|
||||
g1_policy()->max_pause_time_ms(),
|
||||
cause);
|
||||
VMThread::execute(&op);
|
||||
} else {
|
||||
if (cause == GCCause::_gc_locker
|
||||
DEBUG_ONLY(|| cause == GCCause::_scavenge_alot)) {
|
||||
{
|
||||
MutexLocker ml(Heap_lock);
|
||||
|
||||
// Schedule a standard evacuation pause. We're setting word_size
|
||||
// to 0 which means that we are not requesting a post-GC allocation.
|
||||
// Read the GC count while holding the Heap_lock
|
||||
gc_count_before = total_collections();
|
||||
full_gc_count_before = total_full_collections();
|
||||
}
|
||||
|
||||
if (should_do_concurrent_full_gc(cause)) {
|
||||
// Schedule an initial-mark evacuation pause that will start a
|
||||
// concurrent cycle. We're setting word_size to 0 which means that
|
||||
// we are not requesting a post-GC allocation.
|
||||
VM_G1IncCollectionPause op(gc_count_before,
|
||||
0, /* word_size */
|
||||
false, /* should_initiate_conc_mark */
|
||||
true, /* should_initiate_conc_mark */
|
||||
g1_policy()->max_pause_time_ms(),
|
||||
cause);
|
||||
VMThread::execute(&op);
|
||||
if (!op.pause_succeeded()) {
|
||||
// Another GC got scheduled and prevented us from scheduling
|
||||
// the initial-mark GC. It's unlikely that the GC that
|
||||
// pre-empted us was also an initial-mark GC. So, we'll retry
|
||||
// the initial-mark GC.
|
||||
|
||||
if (full_gc_count_before == total_full_collections()) {
|
||||
retry_gc = true;
|
||||
} else {
|
||||
// A Full GC happened while we were trying to schedule the
|
||||
// initial-mark GC. No point in starting a new cycle given
|
||||
// that the whole heap was collected anyway.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Schedule a Full GC.
|
||||
VM_G1CollectFull op(gc_count_before, full_gc_count_before, cause);
|
||||
VMThread::execute(&op);
|
||||
if (cause == GCCause::_gc_locker
|
||||
DEBUG_ONLY(|| cause == GCCause::_scavenge_alot)) {
|
||||
|
||||
// Schedule a standard evacuation pause. We're setting word_size
|
||||
// to 0 which means that we are not requesting a post-GC allocation.
|
||||
VM_G1IncCollectionPause op(gc_count_before,
|
||||
0, /* word_size */
|
||||
false, /* should_initiate_conc_mark */
|
||||
g1_policy()->max_pause_time_ms(),
|
||||
cause);
|
||||
VMThread::execute(&op);
|
||||
} else {
|
||||
// Schedule a Full GC.
|
||||
VM_G1CollectFull op(gc_count_before, full_gc_count_before, cause);
|
||||
VMThread::execute(&op);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (retry_gc);
|
||||
}
|
||||
|
||||
bool G1CollectedHeap::is_in(const void* p) const {
|
||||
@ -3149,12 +3171,12 @@ void G1CollectedHeap::verify(bool allow_dirty,
|
||||
|
||||
// We apply the relevant closures to all the oops in the
|
||||
// system dictionary, the string table and the code cache.
|
||||
const int so = SharedHeap::SO_AllClasses | SharedHeap::SO_Strings | SharedHeap::SO_CodeCache;
|
||||
const int so = SO_AllClasses | SO_Strings | SO_CodeCache;
|
||||
|
||||
process_strong_roots(true, // activate StrongRootsScope
|
||||
true, // we set "collecting perm gen" to true,
|
||||
// so we don't reset the dirty cards in the perm gen.
|
||||
SharedHeap::ScanningOption(so), // roots scanning options
|
||||
ScanningOption(so), // roots scanning options
|
||||
&rootsCl,
|
||||
&blobsCl,
|
||||
&rootsCl);
|
||||
@ -3425,16 +3447,6 @@ G1CollectedHeap::doConcurrentMark() {
|
||||
}
|
||||
}
|
||||
|
||||
double G1CollectedHeap::predict_region_elapsed_time_ms(HeapRegion *hr,
|
||||
bool young) {
|
||||
return _g1_policy->predict_region_elapsed_time_ms(hr, young);
|
||||
}
|
||||
|
||||
void G1CollectedHeap::check_if_region_is_too_expensive(double
|
||||
predicted_time_ms) {
|
||||
_g1_policy->check_if_region_is_too_expensive(predicted_time_ms);
|
||||
}
|
||||
|
||||
size_t G1CollectedHeap::pending_card_num() {
|
||||
size_t extra_cards = 0;
|
||||
JavaThread *curr = Threads::first();
|
||||
@ -3706,12 +3718,12 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
|
||||
g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty);
|
||||
#endif // YOUNG_LIST_VERBOSE
|
||||
|
||||
g1_policy()->choose_collection_set(target_pause_time_ms);
|
||||
g1_policy()->finalize_cset(target_pause_time_ms);
|
||||
|
||||
_cm->note_start_of_gc();
|
||||
// We should not verify the per-thread SATB buffers given that
|
||||
// we have not filtered them yet (we'll do so during the
|
||||
// GC). We also call this after choose_collection_set() to
|
||||
// GC). We also call this after finalize_cset() to
|
||||
// ensure that the CSet has been finalized.
|
||||
_cm->verify_no_cset_oops(true /* verify_stacks */,
|
||||
true /* verify_enqueued_buffers */,
|
||||
@ -4734,7 +4746,7 @@ public:
|
||||
void
|
||||
G1CollectedHeap::
|
||||
g1_process_strong_roots(bool collecting_perm_gen,
|
||||
SharedHeap::ScanningOption so,
|
||||
ScanningOption so,
|
||||
OopClosure* scan_non_heap_roots,
|
||||
OopsInHeapRegionClosure* scan_rs,
|
||||
OopsInGenClosure* scan_perm,
|
||||
|
||||
@ -770,7 +770,7 @@ protected:
|
||||
// the "i" of the calling parallel worker thread's work(i) function.
|
||||
// In the sequential case this param will be ignored.
|
||||
void g1_process_strong_roots(bool collecting_perm_gen,
|
||||
SharedHeap::ScanningOption so,
|
||||
ScanningOption so,
|
||||
OopClosure* scan_non_heap_roots,
|
||||
OopsInHeapRegionClosure* scan_rs,
|
||||
OopsInGenClosure* scan_perm,
|
||||
@ -1182,6 +1182,12 @@ public:
|
||||
bool free_regions_coming() { return _free_regions_coming; }
|
||||
void wait_while_free_regions_coming();
|
||||
|
||||
// Determine whether the given region is one that we are using as an
|
||||
// old GC alloc region.
|
||||
bool is_old_gc_alloc_region(HeapRegion* hr) {
|
||||
return hr == _retained_old_gc_alloc_region;
|
||||
}
|
||||
|
||||
// Perform a collection of the heap; intended for use in implementing
|
||||
// "System.gc". This probably implies as full a collection as the
|
||||
// "CollectedHeap" supports.
|
||||
@ -1662,8 +1668,6 @@ public:
|
||||
public:
|
||||
void stop_conc_gc_threads();
|
||||
|
||||
double predict_region_elapsed_time_ms(HeapRegion* hr, bool young);
|
||||
void check_if_region_is_too_expensive(double predicted_time_ms);
|
||||
size_t pending_card_num();
|
||||
size_t max_pending_card_num();
|
||||
size_t cards_scanned();
|
||||
|
||||
@ -206,7 +206,6 @@ G1CollectorPolicy::G1CollectorPolicy() :
|
||||
|
||||
_initiate_conc_mark_if_possible(false),
|
||||
_during_initial_mark_pause(false),
|
||||
_should_revert_to_young_gcs(false),
|
||||
_last_young_gc(false),
|
||||
_last_gc_was_young(false),
|
||||
|
||||
@ -295,9 +294,6 @@ G1CollectorPolicy::G1CollectorPolicy() :
|
||||
_par_last_gc_worker_times_ms = new double[_parallel_gc_threads];
|
||||
_par_last_gc_worker_other_times_ms = new double[_parallel_gc_threads];
|
||||
|
||||
// start conservatively
|
||||
_expensive_region_limit_ms = 0.5 * (double) MaxGCPauseMillis;
|
||||
|
||||
int index;
|
||||
if (ParallelGCThreads == 0)
|
||||
index = 0;
|
||||
@ -629,16 +625,9 @@ void G1CollectorPolicy::update_young_list_target_length(size_t rs_lengths) {
|
||||
// possible to maximize how many old regions we can add to it.
|
||||
}
|
||||
} else {
|
||||
if (gcs_are_young()) {
|
||||
young_list_target_length = _young_list_fixed_length;
|
||||
} else {
|
||||
// A bit arbitrary: during mixed GCs we allocate half
|
||||
// the young regions to try to add old regions to the CSet.
|
||||
young_list_target_length = _young_list_fixed_length / 2;
|
||||
// We choose to accept that we might go under the desired min
|
||||
// length given that we intentionally ask for a smaller young gen.
|
||||
desired_min_length = absolute_min_length;
|
||||
}
|
||||
// The user asked for a fixed young gen so we'll fix the young gen
|
||||
// whether the next GC is young or mixed.
|
||||
young_list_target_length = _young_list_fixed_length;
|
||||
}
|
||||
|
||||
// Make sure we don't go over the desired max length, nor under the
|
||||
@ -872,7 +861,6 @@ void G1CollectorPolicy::record_full_collection_end() {
|
||||
// transitions and make sure we start with young GCs after the Full GC.
|
||||
set_gcs_are_young(true);
|
||||
_last_young_gc = false;
|
||||
_should_revert_to_young_gcs = false;
|
||||
clear_initiate_conc_mark_if_possible();
|
||||
clear_during_initial_mark_pause();
|
||||
_known_garbage_bytes = 0;
|
||||
@ -889,7 +877,7 @@ void G1CollectorPolicy::record_full_collection_end() {
|
||||
// Reset survivors SurvRateGroup.
|
||||
_survivor_surv_rate_group->reset();
|
||||
update_young_list_target_length();
|
||||
_collectionSetChooser->updateAfterFullCollection();
|
||||
_collectionSetChooser->clearMarkedHeapRegions();
|
||||
}
|
||||
|
||||
void G1CollectorPolicy::record_stop_world_start() {
|
||||
@ -1000,7 +988,6 @@ void G1CollectorPolicy::record_concurrent_mark_cleanup_start() {
|
||||
}
|
||||
|
||||
void G1CollectorPolicy::record_concurrent_mark_cleanup_completed() {
|
||||
_should_revert_to_young_gcs = false;
|
||||
_last_young_gc = true;
|
||||
_in_marking_window = false;
|
||||
}
|
||||
@ -1205,9 +1192,7 @@ void G1CollectorPolicy::record_collection_pause_end(int no_of_gc_threads) {
|
||||
last_pause_included_initial_mark = during_initial_mark_pause();
|
||||
if (last_pause_included_initial_mark) {
|
||||
record_concurrent_mark_init_end(0.0);
|
||||
}
|
||||
|
||||
if (!_last_young_gc && need_to_start_conc_mark("end of GC")) {
|
||||
} else if (!_last_young_gc && need_to_start_conc_mark("end of GC")) {
|
||||
// Note: this might have already been set, if during the last
|
||||
// pause we decided to start a cycle but at the beginning of
|
||||
// this pause we decided to postpone it. That's OK.
|
||||
@ -1492,12 +1477,14 @@ void G1CollectorPolicy::record_collection_pause_end(int no_of_gc_threads) {
|
||||
}
|
||||
|
||||
if (_last_young_gc) {
|
||||
// This is supposed to to be the "last young GC" before we start
|
||||
// doing mixed GCs. Here we decide whether to start mixed GCs or not.
|
||||
|
||||
if (!last_pause_included_initial_mark) {
|
||||
ergo_verbose2(ErgoMixedGCs,
|
||||
"start mixed GCs",
|
||||
ergo_format_byte_perc("known garbage"),
|
||||
_known_garbage_bytes, _known_garbage_ratio * 100.0);
|
||||
set_gcs_are_young(false);
|
||||
if (next_gc_should_be_mixed("start mixed GCs",
|
||||
"do not start mixed GCs")) {
|
||||
set_gcs_are_young(false);
|
||||
}
|
||||
} else {
|
||||
ergo_verbose0(ErgoMixedGCs,
|
||||
"do not start mixed GCs",
|
||||
@ -1507,39 +1494,14 @@ void G1CollectorPolicy::record_collection_pause_end(int no_of_gc_threads) {
|
||||
}
|
||||
|
||||
if (!_last_gc_was_young) {
|
||||
if (_should_revert_to_young_gcs) {
|
||||
ergo_verbose2(ErgoMixedGCs,
|
||||
"end mixed GCs",
|
||||
ergo_format_reason("mixed GCs end requested")
|
||||
ergo_format_byte_perc("known garbage"),
|
||||
_known_garbage_bytes, _known_garbage_ratio * 100.0);
|
||||
set_gcs_are_young(true);
|
||||
} else if (_known_garbage_ratio < 0.05) {
|
||||
ergo_verbose3(ErgoMixedGCs,
|
||||
"end mixed GCs",
|
||||
ergo_format_reason("known garbage percent lower than threshold")
|
||||
ergo_format_byte_perc("known garbage")
|
||||
ergo_format_perc("threshold"),
|
||||
_known_garbage_bytes, _known_garbage_ratio * 100.0,
|
||||
0.05 * 100.0);
|
||||
set_gcs_are_young(true);
|
||||
} else if (adaptive_young_list_length() &&
|
||||
(get_gc_eff_factor() * cur_efficiency < predict_young_gc_eff())) {
|
||||
ergo_verbose5(ErgoMixedGCs,
|
||||
"end mixed GCs",
|
||||
ergo_format_reason("current GC efficiency lower than "
|
||||
"predicted young GC efficiency")
|
||||
ergo_format_double("GC efficiency factor")
|
||||
ergo_format_double("current GC efficiency")
|
||||
ergo_format_double("predicted young GC efficiency")
|
||||
ergo_format_byte_perc("known garbage"),
|
||||
get_gc_eff_factor(), cur_efficiency,
|
||||
predict_young_gc_eff(),
|
||||
_known_garbage_bytes, _known_garbage_ratio * 100.0);
|
||||
// This is a mixed GC. Here we decide whether to continue doing
|
||||
// mixed GCs or not.
|
||||
|
||||
if (!next_gc_should_be_mixed("continue mixed GCs",
|
||||
"do not continue mixed GCs")) {
|
||||
set_gcs_are_young(true);
|
||||
}
|
||||
}
|
||||
_should_revert_to_young_gcs = false;
|
||||
|
||||
if (_last_gc_was_young && !_during_marking) {
|
||||
_young_gc_eff_seq->add(cur_efficiency);
|
||||
@ -1648,15 +1610,6 @@ void G1CollectorPolicy::record_collection_pause_end(int no_of_gc_threads) {
|
||||
|
||||
_pending_cards_seq->add((double) _pending_cards);
|
||||
_rs_lengths_seq->add((double) _max_rs_lengths);
|
||||
|
||||
double expensive_region_limit_ms =
|
||||
(double) MaxGCPauseMillis - predict_constant_other_time_ms();
|
||||
if (expensive_region_limit_ms < 0.0) {
|
||||
// this means that the other time was predicted to be longer than
|
||||
// than the max pause time
|
||||
expensive_region_limit_ms = (double) MaxGCPauseMillis;
|
||||
}
|
||||
_expensive_region_limit_ms = expensive_region_limit_ms;
|
||||
}
|
||||
|
||||
_in_marking_window = new_in_marking_window;
|
||||
@ -1838,13 +1791,11 @@ G1CollectorPolicy::predict_bytes_to_copy(HeapRegion* hr) {
|
||||
if (hr->is_marked())
|
||||
bytes_to_copy = hr->max_live_bytes();
|
||||
else {
|
||||
guarantee( hr->is_young() && hr->age_in_surv_rate_group() != -1,
|
||||
"invariant" );
|
||||
assert(hr->is_young() && hr->age_in_surv_rate_group() != -1, "invariant");
|
||||
int age = hr->age_in_surv_rate_group();
|
||||
double yg_surv_rate = predict_yg_surv_rate(age, hr->surv_rate_group());
|
||||
bytes_to_copy = (size_t) ((double) hr->used() * yg_surv_rate);
|
||||
}
|
||||
|
||||
return bytes_to_copy;
|
||||
}
|
||||
|
||||
@ -1860,22 +1811,6 @@ void G1CollectorPolicy::set_recorded_rs_lengths(size_t rs_lengths) {
|
||||
_recorded_rs_lengths = rs_lengths;
|
||||
}
|
||||
|
||||
void G1CollectorPolicy::check_if_region_is_too_expensive(double
|
||||
predicted_time_ms) {
|
||||
// I don't think we need to do this when in young GC mode since
|
||||
// marking will be initiated next time we hit the soft limit anyway...
|
||||
if (predicted_time_ms > _expensive_region_limit_ms) {
|
||||
ergo_verbose2(ErgoMixedGCs,
|
||||
"request mixed GCs end",
|
||||
ergo_format_reason("predicted region time higher than threshold")
|
||||
ergo_format_ms("predicted region time")
|
||||
ergo_format_ms("threshold"),
|
||||
predicted_time_ms, _expensive_region_limit_ms);
|
||||
// no point in doing another mixed GC
|
||||
_should_revert_to_young_gcs = true;
|
||||
}
|
||||
}
|
||||
|
||||
void G1CollectorPolicy::update_recent_gc_times(double end_time_sec,
|
||||
double elapsed_ms) {
|
||||
_recent_gc_times_ms->add(elapsed_ms);
|
||||
@ -2274,12 +2209,12 @@ G1CollectorPolicy::decide_on_conc_mark_initiation() {
|
||||
}
|
||||
|
||||
class KnownGarbageClosure: public HeapRegionClosure {
|
||||
G1CollectedHeap* _g1h;
|
||||
CollectionSetChooser* _hrSorted;
|
||||
|
||||
public:
|
||||
KnownGarbageClosure(CollectionSetChooser* hrSorted) :
|
||||
_hrSorted(hrSorted)
|
||||
{}
|
||||
_g1h(G1CollectedHeap::heap()), _hrSorted(hrSorted) { }
|
||||
|
||||
bool doHeapRegion(HeapRegion* r) {
|
||||
// We only include humongous regions in collection
|
||||
@ -2288,11 +2223,10 @@ public:
|
||||
|
||||
// Do we have any marking information for this region?
|
||||
if (r->is_marked()) {
|
||||
// We don't include humongous regions in collection
|
||||
// sets because we collect them immediately at the end of a marking
|
||||
// cycle. We also don't include young regions because we *must*
|
||||
// include them in the next collection pause.
|
||||
if (!r->isHumongous() && !r->is_young()) {
|
||||
// We will skip any region that's currently used as an old GC
|
||||
// alloc region (we should not consider those for collection
|
||||
// before we fill them up).
|
||||
if (_hrSorted->shouldAdd(r) && !_g1h->is_old_gc_alloc_region(r)) {
|
||||
_hrSorted->addMarkedHeapRegion(r);
|
||||
}
|
||||
}
|
||||
@ -2301,8 +2235,10 @@ public:
|
||||
};
|
||||
|
||||
class ParKnownGarbageHRClosure: public HeapRegionClosure {
|
||||
G1CollectedHeap* _g1h;
|
||||
CollectionSetChooser* _hrSorted;
|
||||
jint _marked_regions_added;
|
||||
size_t _reclaimable_bytes_added;
|
||||
jint _chunk_size;
|
||||
jint _cur_chunk_idx;
|
||||
jint _cur_chunk_end; // Cur chunk [_cur_chunk_idx, _cur_chunk_end)
|
||||
@ -2320,6 +2256,7 @@ class ParKnownGarbageHRClosure: public HeapRegionClosure {
|
||||
assert(_cur_chunk_idx < _cur_chunk_end, "postcondition");
|
||||
_hrSorted->setMarkedHeapRegion(_cur_chunk_idx, r);
|
||||
_marked_regions_added++;
|
||||
_reclaimable_bytes_added += r->reclaimable_bytes();
|
||||
_cur_chunk_idx++;
|
||||
}
|
||||
|
||||
@ -2327,10 +2264,10 @@ public:
|
||||
ParKnownGarbageHRClosure(CollectionSetChooser* hrSorted,
|
||||
jint chunk_size,
|
||||
int worker) :
|
||||
_hrSorted(hrSorted), _chunk_size(chunk_size), _worker(worker),
|
||||
_marked_regions_added(0), _cur_chunk_idx(0), _cur_chunk_end(0),
|
||||
_invokes(0)
|
||||
{}
|
||||
_g1h(G1CollectedHeap::heap()),
|
||||
_hrSorted(hrSorted), _chunk_size(chunk_size), _worker(worker),
|
||||
_marked_regions_added(0), _reclaimable_bytes_added(0),
|
||||
_cur_chunk_idx(0), _cur_chunk_end(0), _invokes(0) { }
|
||||
|
||||
bool doHeapRegion(HeapRegion* r) {
|
||||
// We only include humongous regions in collection
|
||||
@ -2340,17 +2277,17 @@ public:
|
||||
|
||||
// Do we have any marking information for this region?
|
||||
if (r->is_marked()) {
|
||||
// We don't include humongous regions in collection
|
||||
// sets because we collect them immediately at the end of a marking
|
||||
// cycle.
|
||||
// We also do not include young regions in collection sets
|
||||
if (!r->isHumongous() && !r->is_young()) {
|
||||
// We will skip any region that's currently used as an old GC
|
||||
// alloc region (we should not consider those for collection
|
||||
// before we fill them up).
|
||||
if (_hrSorted->shouldAdd(r) && !_g1h->is_old_gc_alloc_region(r)) {
|
||||
add_region(r);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
jint marked_regions_added() { return _marked_regions_added; }
|
||||
size_t reclaimable_bytes_added() { return _reclaimable_bytes_added; }
|
||||
int invokes() { return _invokes; }
|
||||
};
|
||||
|
||||
@ -2362,8 +2299,7 @@ public:
|
||||
ParKnownGarbageTask(CollectionSetChooser* hrSorted, jint chunk_size) :
|
||||
AbstractGangTask("ParKnownGarbageTask"),
|
||||
_hrSorted(hrSorted), _chunk_size(chunk_size),
|
||||
_g1(G1CollectedHeap::heap())
|
||||
{}
|
||||
_g1(G1CollectedHeap::heap()) { }
|
||||
|
||||
void work(uint worker_id) {
|
||||
ParKnownGarbageHRClosure parKnownGarbageCl(_hrSorted,
|
||||
@ -2374,7 +2310,9 @@ public:
|
||||
_g1->workers()->active_workers(),
|
||||
HeapRegion::InitialClaimValue);
|
||||
jint regions_added = parKnownGarbageCl.marked_regions_added();
|
||||
_hrSorted->incNumMarkedHeapRegions(regions_added);
|
||||
size_t reclaimable_bytes_added =
|
||||
parKnownGarbageCl.reclaimable_bytes_added();
|
||||
_hrSorted->updateTotals(regions_added, reclaimable_bytes_added);
|
||||
if (G1PrintParCleanupStats) {
|
||||
gclog_or_tty->print_cr(" Thread %d called %d times, added %d regions to list.",
|
||||
worker_id, parKnownGarbageCl.invokes(), regions_added);
|
||||
@ -2658,7 +2596,43 @@ void G1CollectorPolicy::print_collection_set(HeapRegion* list_head, outputStream
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
|
||||
void G1CollectorPolicy::choose_collection_set(double target_pause_time_ms) {
|
||||
bool G1CollectorPolicy::next_gc_should_be_mixed(const char* true_action_str,
|
||||
const char* false_action_str) {
|
||||
CollectionSetChooser* cset_chooser = _collectionSetChooser;
|
||||
if (cset_chooser->isEmpty()) {
|
||||
ergo_verbose0(ErgoMixedGCs,
|
||||
false_action_str,
|
||||
ergo_format_reason("candidate old regions not available"));
|
||||
return false;
|
||||
}
|
||||
size_t reclaimable_bytes = cset_chooser->remainingReclaimableBytes();
|
||||
size_t capacity_bytes = _g1->capacity();
|
||||
double perc = (double) reclaimable_bytes * 100.0 / (double) capacity_bytes;
|
||||
double threshold = (double) G1OldReclaimableThresholdPercent;
|
||||
if (perc < threshold) {
|
||||
ergo_verbose4(ErgoMixedGCs,
|
||||
false_action_str,
|
||||
ergo_format_reason("reclaimable percentage lower than threshold")
|
||||
ergo_format_region("candidate old regions")
|
||||
ergo_format_byte_perc("reclaimable")
|
||||
ergo_format_perc("threshold"),
|
||||
cset_chooser->remainingRegions(),
|
||||
reclaimable_bytes, perc, threshold);
|
||||
return false;
|
||||
}
|
||||
|
||||
ergo_verbose4(ErgoMixedGCs,
|
||||
true_action_str,
|
||||
ergo_format_reason("candidate old regions available")
|
||||
ergo_format_region("candidate old regions")
|
||||
ergo_format_byte_perc("reclaimable")
|
||||
ergo_format_perc("threshold"),
|
||||
cset_chooser->remainingRegions(),
|
||||
reclaimable_bytes, perc, threshold);
|
||||
return true;
|
||||
}
|
||||
|
||||
void G1CollectorPolicy::finalize_cset(double target_pause_time_ms) {
|
||||
// Set this here - in case we're not doing young collections.
|
||||
double non_young_start_time_sec = os::elapsedTime();
|
||||
|
||||
@ -2672,7 +2646,6 @@ void G1CollectorPolicy::choose_collection_set(double target_pause_time_ms) {
|
||||
|
||||
double base_time_ms = predict_base_elapsed_time_ms(_pending_cards);
|
||||
double predicted_pause_time_ms = base_time_ms;
|
||||
|
||||
double time_remaining_ms = target_pause_time_ms - base_time_ms;
|
||||
|
||||
ergo_verbose3(ErgoCSetConstruction | ErgoHigh,
|
||||
@ -2682,22 +2655,6 @@ void G1CollectorPolicy::choose_collection_set(double target_pause_time_ms) {
|
||||
ergo_format_ms("target pause time"),
|
||||
base_time_ms, time_remaining_ms, target_pause_time_ms);
|
||||
|
||||
// the 10% and 50% values are arbitrary...
|
||||
double threshold = 0.10 * target_pause_time_ms;
|
||||
if (time_remaining_ms < threshold) {
|
||||
double prev_time_remaining_ms = time_remaining_ms;
|
||||
time_remaining_ms = 0.50 * target_pause_time_ms;
|
||||
ergo_verbose3(ErgoCSetConstruction,
|
||||
"adjust remaining time",
|
||||
ergo_format_reason("remaining time lower than threshold")
|
||||
ergo_format_ms("remaining time")
|
||||
ergo_format_ms("threshold")
|
||||
ergo_format_ms("adjusted remaining time"),
|
||||
prev_time_remaining_ms, threshold, time_remaining_ms);
|
||||
}
|
||||
|
||||
size_t expansion_bytes = _g1->expansion_regions() * HeapRegion::GrainBytes;
|
||||
|
||||
HeapRegion* hr;
|
||||
double young_start_time_sec = os::elapsedTime();
|
||||
|
||||
@ -2752,78 +2709,97 @@ void G1CollectorPolicy::choose_collection_set(double target_pause_time_ms) {
|
||||
non_young_start_time_sec = young_end_time_sec;
|
||||
|
||||
if (!gcs_are_young()) {
|
||||
bool should_continue = true;
|
||||
NumberSeq seq;
|
||||
double avg_prediction = 100000000000000000.0; // something very large
|
||||
CollectionSetChooser* cset_chooser = _collectionSetChooser;
|
||||
assert(cset_chooser->verify(), "CSet Chooser verification - pre");
|
||||
const size_t min_old_cset_length = cset_chooser->calcMinOldCSetLength();
|
||||
const size_t max_old_cset_length = cset_chooser->calcMaxOldCSetLength();
|
||||
|
||||
double prev_predicted_pause_time_ms = predicted_pause_time_ms;
|
||||
do {
|
||||
// Note that add_old_region_to_cset() increments the
|
||||
// _old_cset_region_length field and cset_region_length() returns the
|
||||
// sum of _eden_cset_region_length, _survivor_cset_region_length, and
|
||||
// _old_cset_region_length. So, as old regions are added to the
|
||||
// CSet, _old_cset_region_length will be incremented and
|
||||
// cset_region_length(), which is used below, will always reflect
|
||||
// the the total number of regions added up to this point to the CSet.
|
||||
|
||||
hr = _collectionSetChooser->getNextMarkedRegion(time_remaining_ms,
|
||||
avg_prediction);
|
||||
if (hr != NULL) {
|
||||
_g1->old_set_remove(hr);
|
||||
double predicted_time_ms = predict_region_elapsed_time_ms(hr, false);
|
||||
time_remaining_ms -= predicted_time_ms;
|
||||
predicted_pause_time_ms += predicted_time_ms;
|
||||
add_old_region_to_cset(hr);
|
||||
seq.add(predicted_time_ms);
|
||||
avg_prediction = seq.avg() + seq.sd();
|
||||
size_t expensive_region_num = 0;
|
||||
bool check_time_remaining = adaptive_young_list_length();
|
||||
HeapRegion* hr = cset_chooser->peek();
|
||||
while (hr != NULL) {
|
||||
if (old_cset_region_length() >= max_old_cset_length) {
|
||||
// Added maximum number of old regions to the CSet.
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"finish adding old regions to CSet",
|
||||
ergo_format_reason("old CSet region num reached max")
|
||||
ergo_format_region("old")
|
||||
ergo_format_region("max"),
|
||||
old_cset_region_length(), max_old_cset_length);
|
||||
break;
|
||||
}
|
||||
|
||||
should_continue = true;
|
||||
if (hr == NULL) {
|
||||
// No need for an ergo verbose message here,
|
||||
// getNextMarkRegion() does this when it returns NULL.
|
||||
should_continue = false;
|
||||
double predicted_time_ms = predict_region_elapsed_time_ms(hr, false);
|
||||
if (check_time_remaining) {
|
||||
if (predicted_time_ms > time_remaining_ms) {
|
||||
// Too expensive for the current CSet.
|
||||
|
||||
if (old_cset_region_length() >= min_old_cset_length) {
|
||||
// We have added the minimum number of old regions to the CSet,
|
||||
// we are done with this CSet.
|
||||
ergo_verbose4(ErgoCSetConstruction,
|
||||
"finish adding old regions to CSet",
|
||||
ergo_format_reason("predicted time is too high")
|
||||
ergo_format_ms("predicted time")
|
||||
ergo_format_ms("remaining time")
|
||||
ergo_format_region("old")
|
||||
ergo_format_region("min"),
|
||||
predicted_time_ms, time_remaining_ms,
|
||||
old_cset_region_length(), min_old_cset_length);
|
||||
break;
|
||||
}
|
||||
|
||||
// We'll add it anyway given that we haven't reached the
|
||||
// minimum number of old regions.
|
||||
expensive_region_num += 1;
|
||||
}
|
||||
} else {
|
||||
if (adaptive_young_list_length()) {
|
||||
if (time_remaining_ms < 0.0) {
|
||||
ergo_verbose1(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("remaining time is lower than 0")
|
||||
ergo_format_ms("remaining time"),
|
||||
time_remaining_ms);
|
||||
should_continue = false;
|
||||
}
|
||||
} else {
|
||||
if (cset_region_length() >= _young_list_fixed_length) {
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"stop adding old regions to CSet",
|
||||
ergo_format_reason("CSet length reached target")
|
||||
ergo_format_region("CSet")
|
||||
ergo_format_region("young target"),
|
||||
cset_region_length(), _young_list_fixed_length);
|
||||
should_continue = false;
|
||||
}
|
||||
if (old_cset_region_length() >= min_old_cset_length) {
|
||||
// In the non-auto-tuning case, we'll finish adding regions
|
||||
// to the CSet if we reach the minimum.
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"finish adding old regions to CSet",
|
||||
ergo_format_reason("old CSet region num reached min")
|
||||
ergo_format_region("old")
|
||||
ergo_format_region("min"),
|
||||
old_cset_region_length(), min_old_cset_length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (should_continue);
|
||||
|
||||
if (!adaptive_young_list_length() &&
|
||||
cset_region_length() < _young_list_fixed_length) {
|
||||
ergo_verbose2(ErgoCSetConstruction,
|
||||
"request mixed GCs end",
|
||||
ergo_format_reason("CSet length lower than target")
|
||||
ergo_format_region("CSet")
|
||||
ergo_format_region("young target"),
|
||||
cset_region_length(), _young_list_fixed_length);
|
||||
_should_revert_to_young_gcs = true;
|
||||
// We will add this region to the CSet.
|
||||
time_remaining_ms -= predicted_time_ms;
|
||||
predicted_pause_time_ms += predicted_time_ms;
|
||||
cset_chooser->remove_and_move_to_next(hr);
|
||||
_g1->old_set_remove(hr);
|
||||
add_old_region_to_cset(hr);
|
||||
|
||||
hr = cset_chooser->peek();
|
||||
}
|
||||
if (hr == NULL) {
|
||||
ergo_verbose0(ErgoCSetConstruction,
|
||||
"finish adding old regions to CSet",
|
||||
ergo_format_reason("candidate old regions not available"));
|
||||
}
|
||||
|
||||
ergo_verbose2(ErgoCSetConstruction | ErgoHigh,
|
||||
"add old regions to CSet",
|
||||
ergo_format_region("old")
|
||||
ergo_format_ms("predicted old region time"),
|
||||
old_cset_region_length(),
|
||||
predicted_pause_time_ms - prev_predicted_pause_time_ms);
|
||||
if (expensive_region_num > 0) {
|
||||
// We print the information once here at the end, predicated on
|
||||
// whether we added any apparently expensive regions or not, to
|
||||
// avoid generating output per region.
|
||||
ergo_verbose4(ErgoCSetConstruction,
|
||||
"added expensive regions to CSet",
|
||||
ergo_format_reason("old CSet region num not reached min")
|
||||
ergo_format_region("old")
|
||||
ergo_format_region("expensive")
|
||||
ergo_format_region("min")
|
||||
ergo_format_ms("remaining time"),
|
||||
old_cset_region_length(),
|
||||
expensive_region_num,
|
||||
min_old_cset_length,
|
||||
time_remaining_ms);
|
||||
}
|
||||
|
||||
assert(cset_chooser->verify(), "CSet Chooser verification - post");
|
||||
}
|
||||
|
||||
stop_incremental_cset_building();
|
||||
|
||||
@ -312,16 +312,13 @@ private:
|
||||
double _recorded_non_young_free_cset_time_ms;
|
||||
|
||||
double _sigma;
|
||||
double _expensive_region_limit_ms;
|
||||
|
||||
size_t _rs_lengths_prediction;
|
||||
|
||||
size_t _known_garbage_bytes;
|
||||
double _known_garbage_ratio;
|
||||
|
||||
double sigma() {
|
||||
return _sigma;
|
||||
}
|
||||
double sigma() { return _sigma; }
|
||||
|
||||
// A function that prevents us putting too much stock in small sample
|
||||
// sets. Returns a number between 2.0 and 1.0, depending on the number
|
||||
@ -491,8 +488,6 @@ public:
|
||||
get_new_prediction(_non_young_other_cost_per_region_ms_seq);
|
||||
}
|
||||
|
||||
void check_if_region_is_too_expensive(double predicted_time_ms);
|
||||
|
||||
double predict_young_collection_elapsed_time_ms(size_t adjustment);
|
||||
double predict_base_elapsed_time_ms(size_t pending_cards);
|
||||
double predict_base_elapsed_time_ms(size_t pending_cards,
|
||||
@ -707,7 +702,6 @@ private:
|
||||
// initial-mark work.
|
||||
volatile bool _during_initial_mark_pause;
|
||||
|
||||
bool _should_revert_to_young_gcs;
|
||||
bool _last_young_gc;
|
||||
|
||||
// This set of variables tracks the collector efficiency, in order to
|
||||
@ -946,10 +940,17 @@ public:
|
||||
return _bytes_copied_during_gc;
|
||||
}
|
||||
|
||||
// Determine whether the next GC should be mixed. Called to determine
|
||||
// whether to start mixed GCs or whether to carry on doing mixed
|
||||
// GCs. The two action strings are used in the ergo output when the
|
||||
// method returns true or false.
|
||||
bool next_gc_should_be_mixed(const char* true_action_str,
|
||||
const char* false_action_str);
|
||||
|
||||
// Choose a new collection set. Marks the chosen regions as being
|
||||
// "in_collection_set", and links them together. The head and number of
|
||||
// the collection set are available via access methods.
|
||||
void choose_collection_set(double target_pause_time_ms);
|
||||
void finalize_cset(double target_pause_time_ms);
|
||||
|
||||
// The head of the list (via "next_in_collection_set()") representing the
|
||||
// current collection set.
|
||||
|
||||
@ -131,8 +131,8 @@ public:
|
||||
", " _name_ ": "SIZE_FORMAT" bytes (%1.2f %%)"
|
||||
|
||||
// Generates the format string
|
||||
#define ergo_format(_action_, _extra_format_) \
|
||||
" %1.3f: [G1Ergonomics (%s) " _action_ _extra_format_ "]"
|
||||
#define ergo_format(_extra_format_) \
|
||||
" %1.3f: [G1Ergonomics (%s) %s" _extra_format_ "]"
|
||||
|
||||
// Conditionally, prints an ergonomic decision record. _extra_format_
|
||||
// is the format string for the optional items we'd like to print
|
||||
@ -145,20 +145,21 @@ public:
|
||||
// them to the print method. For convenience, we have wrapper macros
|
||||
// below which take a specific number of arguments and set the rest to
|
||||
// a default value.
|
||||
#define ergo_verbose_common(_tag_, _action_, _extra_format_, \
|
||||
#define ergo_verbose_common(_tag_, _action_, _extra_format_, \
|
||||
_arg0_, _arg1_, _arg2_, _arg3_, _arg4_, _arg5_) \
|
||||
do { \
|
||||
if (G1ErgoVerbose::enabled((_tag_))) { \
|
||||
gclog_or_tty->print_cr(ergo_format(_action_, _extra_format_), \
|
||||
os::elapsedTime(), \
|
||||
G1ErgoVerbose::to_string((_tag_)), \
|
||||
(_arg0_), (_arg1_), (_arg2_), \
|
||||
(_arg3_), (_arg4_), (_arg5_)); \
|
||||
} \
|
||||
do { \
|
||||
if (G1ErgoVerbose::enabled((_tag_))) { \
|
||||
gclog_or_tty->print_cr(ergo_format(_extra_format_), \
|
||||
os::elapsedTime(), \
|
||||
G1ErgoVerbose::to_string((_tag_)), \
|
||||
(_action_), \
|
||||
(_arg0_), (_arg1_), (_arg2_), \
|
||||
(_arg3_), (_arg4_), (_arg5_)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define ergo_verbose(_tag_, _action_) \
|
||||
#define ergo_verbose(_tag_, _action_) \
|
||||
ergo_verbose_common(_tag_, _action_, "", 0, 0, 0, 0, 0, 0)
|
||||
|
||||
#define ergo_verbose0(_tag_, _action_, _extra_format_) \
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2012, 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
|
||||
@ -297,7 +297,23 @@
|
||||
\
|
||||
develop(uintx, G1DefaultMaxNewGenPercent, 80, \
|
||||
"Percentage (0-100) of the heap size to use as maximum " \
|
||||
"young gen size.")
|
||||
"young gen size.") \
|
||||
\
|
||||
develop(uintx, G1OldCSetRegionLiveThresholdPercent, 95, \
|
||||
"Threshold for regions to be added to the collection set. " \
|
||||
"Regions with more live bytes that this will not be collected.") \
|
||||
\
|
||||
develop(uintx, G1OldReclaimableThresholdPercent, 1, \
|
||||
"Threshold for the remaining old reclaimable bytes, expressed " \
|
||||
"as a percentage of the heap size. If the old reclaimable bytes " \
|
||||
"are under this we will not collect them with more mixed GCs.") \
|
||||
\
|
||||
develop(uintx, G1MaxMixedGCNum, 4, \
|
||||
"The maximum desired number of mixed GCs after a marking cycle.") \
|
||||
\
|
||||
develop(uintx, G1OldCSetRegionThresholdPercent, 10, \
|
||||
"An upper bound for the number of old CSet regions expressed " \
|
||||
"as a percentage of the heap size.")
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@ -387,13 +387,12 @@ void HeapRegion::par_clear() {
|
||||
ct_bs->clear(MemRegion(bottom(), end()));
|
||||
}
|
||||
|
||||
// <PREDICTION>
|
||||
void HeapRegion::calc_gc_efficiency() {
|
||||
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||
_gc_efficiency = (double) garbage_bytes() /
|
||||
g1h->predict_region_elapsed_time_ms(this, false);
|
||||
G1CollectorPolicy* g1p = g1h->g1_policy();
|
||||
_gc_efficiency = (double) reclaimable_bytes() /
|
||||
g1p->predict_region_elapsed_time_ms(this, false);
|
||||
}
|
||||
// </PREDICTION>
|
||||
|
||||
void HeapRegion::set_startsHumongous(HeapWord* new_top, HeapWord* new_end) {
|
||||
assert(!isHumongous(), "sanity / pre-condition");
|
||||
|
||||
@ -415,6 +415,16 @@ class HeapRegion: public G1OffsetTableContigSpace {
|
||||
return used_at_mark_start_bytes - marked_bytes();
|
||||
}
|
||||
|
||||
// Return the amount of bytes we'll reclaim if we collect this
|
||||
// region. This includes not only the known garbage bytes in the
|
||||
// region but also any unallocated space in it, i.e., [top, end),
|
||||
// since it will also be reclaimed if we collect the region.
|
||||
size_t reclaimable_bytes() {
|
||||
size_t known_live_bytes = live_bytes();
|
||||
assert(known_live_bytes <= capacity(), "sanity");
|
||||
return capacity() - known_live_bytes;
|
||||
}
|
||||
|
||||
// An upper bound on the number of live bytes in the region.
|
||||
size_t max_live_bytes() { return used() - garbage_bytes(); }
|
||||
|
||||
@ -648,10 +658,8 @@ class HeapRegion: public G1OffsetTableContigSpace {
|
||||
init_top_at_mark_start();
|
||||
}
|
||||
|
||||
// <PREDICTION>
|
||||
void calc_gc_efficiency(void);
|
||||
double gc_efficiency() { return _gc_efficiency;}
|
||||
// </PREDICTION>
|
||||
|
||||
bool is_young() const { return _young_type != NotYoung; }
|
||||
bool is_survivor() const { return _young_type == Survivor; }
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2012, 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
|
||||
@ -1042,7 +1042,11 @@ void ParNewGeneration::collect(bool full,
|
||||
size_policy->avg_survived()->sample(from()->used());
|
||||
}
|
||||
|
||||
update_time_of_last_gc(os::javaTimeMillis());
|
||||
// We need to use a monotonically non-deccreasing time in ms
|
||||
// or we will see time-warp warnings and os::javaTimeMillis()
|
||||
// does not guarantee monotonicity.
|
||||
jlong now = os::javaTimeNanos() / NANOSECS_PER_MILLISEC;
|
||||
update_time_of_last_gc(now);
|
||||
|
||||
SpecializationStats::print();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2012, 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
|
||||
@ -418,25 +418,17 @@ HeapWord* ParallelScavengeHeap::mem_allocate(
|
||||
gc_count = Universe::heap()->total_collections();
|
||||
|
||||
result = young_gen()->allocate(size);
|
||||
|
||||
// (1) If the requested object is too large to easily fit in the
|
||||
// young_gen, or
|
||||
// (2) If GC is locked out via GCLocker, young gen is full and
|
||||
// the need for a GC already signalled to GCLocker (done
|
||||
// at a safepoint),
|
||||
// ... then, rather than force a safepoint and (a potentially futile)
|
||||
// collection (attempt) for each allocation, try allocation directly
|
||||
// in old_gen. For case (2) above, we may in the future allow
|
||||
// TLAB allocation directly in the old gen.
|
||||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
if (size >= (young_gen()->eden_space()->capacity_in_words(Thread::current()) / 2)) {
|
||||
result = old_gen()->allocate(size);
|
||||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// If certain conditions hold, try allocating from the old gen.
|
||||
result = mem_allocate_old_gen(size);
|
||||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Failed to allocate without a gc.
|
||||
if (GC_locker::is_active_and_needs_gc()) {
|
||||
// If this thread is not in a jni critical section, we stall
|
||||
// the requestor until the critical section has cleared and
|
||||
@ -460,7 +452,6 @@ HeapWord* ParallelScavengeHeap::mem_allocate(
|
||||
}
|
||||
|
||||
if (result == NULL) {
|
||||
|
||||
// Generate a VM operation
|
||||
VM_ParallelGCFailedAllocation op(size, gc_count);
|
||||
VMThread::execute(&op);
|
||||
@ -523,6 +514,42 @@ HeapWord* ParallelScavengeHeap::mem_allocate(
|
||||
return result;
|
||||
}
|
||||
|
||||
// A "death march" is a series of ultra-slow allocations in which a full gc is
|
||||
// done before each allocation, and after the full gc the allocation still
|
||||
// cannot be satisfied from the young gen. This routine detects that condition;
|
||||
// it should be called after a full gc has been done and the allocation
|
||||
// attempted from the young gen. The parameter 'addr' should be the result of
|
||||
// that young gen allocation attempt.
|
||||
void
|
||||
ParallelScavengeHeap::death_march_check(HeapWord* const addr, size_t size) {
|
||||
if (addr != NULL) {
|
||||
_death_march_count = 0; // death march has ended
|
||||
} else if (_death_march_count == 0) {
|
||||
if (should_alloc_in_eden(size)) {
|
||||
_death_march_count = 1; // death march has started
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HeapWord* ParallelScavengeHeap::mem_allocate_old_gen(size_t size) {
|
||||
if (!should_alloc_in_eden(size) || GC_locker::is_active_and_needs_gc()) {
|
||||
// Size is too big for eden, or gc is locked out.
|
||||
return old_gen()->allocate(size);
|
||||
}
|
||||
|
||||
// If a "death march" is in progress, allocate from the old gen a limited
|
||||
// number of times before doing a GC.
|
||||
if (_death_march_count > 0) {
|
||||
if (_death_march_count < 64) {
|
||||
++_death_march_count;
|
||||
return old_gen()->allocate(size);
|
||||
} else {
|
||||
_death_march_count = 0;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Failed allocation policy. Must be called from the VM thread, and
|
||||
// only at a safepoint! Note that this method has policy for allocation
|
||||
// flow, and NOT collection policy. So we do not check for gc collection
|
||||
@ -535,27 +562,22 @@ HeapWord* ParallelScavengeHeap::failed_mem_allocate(size_t size) {
|
||||
assert(!Universe::heap()->is_gc_active(), "not reentrant");
|
||||
assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock");
|
||||
|
||||
size_t mark_sweep_invocation_count = total_invocations();
|
||||
|
||||
// We assume (and assert!) that an allocation at this point will fail
|
||||
// unless we collect.
|
||||
// We assume that allocation in eden will fail unless we collect.
|
||||
|
||||
// First level allocation failure, scavenge and allocate in young gen.
|
||||
GCCauseSetter gccs(this, GCCause::_allocation_failure);
|
||||
PSScavenge::invoke();
|
||||
const bool invoked_full_gc = PSScavenge::invoke();
|
||||
HeapWord* result = young_gen()->allocate(size);
|
||||
|
||||
// Second level allocation failure.
|
||||
// Mark sweep and allocate in young generation.
|
||||
if (result == NULL) {
|
||||
// There is some chance the scavenge method decided to invoke mark_sweep.
|
||||
// Don't mark sweep twice if so.
|
||||
if (mark_sweep_invocation_count == total_invocations()) {
|
||||
invoke_full_gc(false);
|
||||
result = young_gen()->allocate(size);
|
||||
}
|
||||
if (result == NULL && !invoked_full_gc) {
|
||||
invoke_full_gc(false);
|
||||
result = young_gen()->allocate(size);
|
||||
}
|
||||
|
||||
death_march_check(result, size);
|
||||
|
||||
// Third level allocation failure.
|
||||
// After mark sweep and young generation allocation failure,
|
||||
// allocate in old generation.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2012, 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
|
||||
@ -64,6 +64,7 @@ class ParallelScavengeHeap : public CollectedHeap {
|
||||
// Collection of generations that are adjacent in the
|
||||
// space reserved for the heap.
|
||||
AdjoiningGenerations* _gens;
|
||||
unsigned int _death_march_count;
|
||||
|
||||
static GCTaskManager* _gc_task_manager; // The task manager.
|
||||
|
||||
@ -71,8 +72,13 @@ class ParallelScavengeHeap : public CollectedHeap {
|
||||
static inline size_t total_invocations();
|
||||
HeapWord* allocate_new_tlab(size_t size);
|
||||
|
||||
inline bool should_alloc_in_eden(size_t size) const;
|
||||
inline void death_march_check(HeapWord* const result, size_t size);
|
||||
HeapWord* mem_allocate_old_gen(size_t size);
|
||||
|
||||
public:
|
||||
ParallelScavengeHeap() : CollectedHeap() {
|
||||
_death_march_count = 0;
|
||||
set_alignment(_perm_gen_alignment, intra_heap_alignment());
|
||||
set_alignment(_young_gen_alignment, intra_heap_alignment());
|
||||
set_alignment(_old_gen_alignment, intra_heap_alignment());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2006, 2012, 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
|
||||
@ -36,6 +36,12 @@ inline size_t ParallelScavengeHeap::total_invocations()
|
||||
PSMarkSweep::total_invocations();
|
||||
}
|
||||
|
||||
inline bool ParallelScavengeHeap::should_alloc_in_eden(const size_t size) const
|
||||
{
|
||||
const size_t eden_size = young_gen()->eden_space()->capacity_in_words();
|
||||
return size < eden_size / 2;
|
||||
}
|
||||
|
||||
inline void ParallelScavengeHeap::invoke_scavenge()
|
||||
{
|
||||
PSScavenge::invoke();
|
||||
|
||||
@ -100,12 +100,12 @@ void PSMarkSweep::invoke(bool maximum_heap_compaction) {
|
||||
|
||||
// This method contains no policy. You should probably
|
||||
// be calling invoke() instead.
|
||||
void PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) {
|
||||
bool PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
|
||||
assert(ref_processor() != NULL, "Sanity");
|
||||
|
||||
if (GC_locker::check_active_before_gc()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
|
||||
@ -382,6 +382,8 @@ void PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) {
|
||||
#ifdef TRACESPINNING
|
||||
ParallelTaskTerminator::print_termination_counts();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PSMarkSweep::absorb_live_data_from_eden(PSAdaptiveSizePolicy* size_policy,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2012, 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
|
||||
@ -78,7 +78,7 @@ class PSMarkSweep : public MarkSweep {
|
||||
|
||||
public:
|
||||
static void invoke(bool clear_all_softrefs);
|
||||
static void invoke_no_policy(bool clear_all_softrefs);
|
||||
static bool invoke_no_policy(bool clear_all_softrefs);
|
||||
|
||||
static void initialize();
|
||||
|
||||
|
||||
@ -1993,12 +1993,12 @@ bool ParallelCompactData::region_contains(size_t region_index, HeapWord* addr) {
|
||||
|
||||
// This method contains no policy. You should probably
|
||||
// be calling invoke() instead.
|
||||
void PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) {
|
||||
bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
|
||||
assert(ref_processor() != NULL, "Sanity");
|
||||
|
||||
if (GC_locker::check_active_before_gc()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeStamp marking_start;
|
||||
@ -2248,6 +2248,8 @@ void PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) {
|
||||
#ifdef TRACESPINNING
|
||||
ParallelTaskTerminator::print_termination_counts();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PSParallelCompact::absorb_live_data_from_eden(PSAdaptiveSizePolicy* size_policy,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2012, 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
|
||||
@ -1057,7 +1057,7 @@ class PSParallelCompact : AllStatic {
|
||||
}
|
||||
|
||||
static void invoke(bool maximum_heap_compaction);
|
||||
static void invoke_no_policy(bool maximum_heap_compaction);
|
||||
static bool invoke_no_policy(bool maximum_heap_compaction);
|
||||
|
||||
static void post_initialize();
|
||||
// Perform initialization for PSParallelCompact that requires
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2012, 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
|
||||
@ -247,167 +247,6 @@ void PSPromotionManager::flush_labs() {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This method is pretty bulky. It would be nice to split it up
|
||||
// into smaller submethods, but we need to be careful not to hurt
|
||||
// performance.
|
||||
//
|
||||
|
||||
oop PSPromotionManager::copy_to_survivor_space(oop o) {
|
||||
assert(PSScavenge::should_scavenge(&o), "Sanity");
|
||||
|
||||
oop new_obj = NULL;
|
||||
|
||||
// NOTE! We must be very careful with any methods that access the mark
|
||||
// in o. There may be multiple threads racing on it, and it may be forwarded
|
||||
// at any time. Do not use oop methods for accessing the mark!
|
||||
markOop test_mark = o->mark();
|
||||
|
||||
// The same test as "o->is_forwarded()"
|
||||
if (!test_mark->is_marked()) {
|
||||
bool new_obj_is_tenured = false;
|
||||
size_t new_obj_size = o->size();
|
||||
|
||||
// Find the objects age, MT safe.
|
||||
int age = (test_mark->has_displaced_mark_helper() /* o->has_displaced_mark() */) ?
|
||||
test_mark->displaced_mark_helper()->age() : test_mark->age();
|
||||
|
||||
// Try allocating obj in to-space (unless too old)
|
||||
if (age < PSScavenge::tenuring_threshold()) {
|
||||
new_obj = (oop) _young_lab.allocate(new_obj_size);
|
||||
if (new_obj == NULL && !_young_gen_is_full) {
|
||||
// Do we allocate directly, or flush and refill?
|
||||
if (new_obj_size > (YoungPLABSize / 2)) {
|
||||
// Allocate this object directly
|
||||
new_obj = (oop)young_space()->cas_allocate(new_obj_size);
|
||||
} else {
|
||||
// Flush and fill
|
||||
_young_lab.flush();
|
||||
|
||||
HeapWord* lab_base = young_space()->cas_allocate(YoungPLABSize);
|
||||
if (lab_base != NULL) {
|
||||
_young_lab.initialize(MemRegion(lab_base, YoungPLABSize));
|
||||
// Try the young lab allocation again.
|
||||
new_obj = (oop) _young_lab.allocate(new_obj_size);
|
||||
} else {
|
||||
_young_gen_is_full = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise try allocating obj tenured
|
||||
if (new_obj == NULL) {
|
||||
#ifndef PRODUCT
|
||||
if (Universe::heap()->promotion_should_fail()) {
|
||||
return oop_promotion_failed(o, test_mark);
|
||||
}
|
||||
#endif // #ifndef PRODUCT
|
||||
|
||||
new_obj = (oop) _old_lab.allocate(new_obj_size);
|
||||
new_obj_is_tenured = true;
|
||||
|
||||
if (new_obj == NULL) {
|
||||
if (!_old_gen_is_full) {
|
||||
// Do we allocate directly, or flush and refill?
|
||||
if (new_obj_size > (OldPLABSize / 2)) {
|
||||
// Allocate this object directly
|
||||
new_obj = (oop)old_gen()->cas_allocate(new_obj_size);
|
||||
} else {
|
||||
// Flush and fill
|
||||
_old_lab.flush();
|
||||
|
||||
HeapWord* lab_base = old_gen()->cas_allocate(OldPLABSize);
|
||||
if(lab_base != NULL) {
|
||||
_old_lab.initialize(MemRegion(lab_base, OldPLABSize));
|
||||
// Try the old lab allocation again.
|
||||
new_obj = (oop) _old_lab.allocate(new_obj_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is the promotion failed test, and code handling.
|
||||
// The code belongs here for two reasons. It is slightly
|
||||
// different thatn the code below, and cannot share the
|
||||
// CAS testing code. Keeping the code here also minimizes
|
||||
// the impact on the common case fast path code.
|
||||
|
||||
if (new_obj == NULL) {
|
||||
_old_gen_is_full = true;
|
||||
return oop_promotion_failed(o, test_mark);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(new_obj != NULL, "allocation should have succeeded");
|
||||
|
||||
// Copy obj
|
||||
Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)new_obj, new_obj_size);
|
||||
|
||||
// Now we have to CAS in the header.
|
||||
if (o->cas_forward_to(new_obj, test_mark)) {
|
||||
// We won any races, we "own" this object.
|
||||
assert(new_obj == o->forwardee(), "Sanity");
|
||||
|
||||
// Increment age if obj still in new generation. Now that
|
||||
// we're dealing with a markOop that cannot change, it is
|
||||
// okay to use the non mt safe oop methods.
|
||||
if (!new_obj_is_tenured) {
|
||||
new_obj->incr_age();
|
||||
assert(young_space()->contains(new_obj), "Attempt to push non-promoted obj");
|
||||
}
|
||||
|
||||
// Do the size comparison first with new_obj_size, which we
|
||||
// already have. Hopefully, only a few objects are larger than
|
||||
// _min_array_size_for_chunking, and most of them will be arrays.
|
||||
// So, the is->objArray() test would be very infrequent.
|
||||
if (new_obj_size > _min_array_size_for_chunking &&
|
||||
new_obj->is_objArray() &&
|
||||
PSChunkLargeArrays) {
|
||||
// we'll chunk it
|
||||
oop* const masked_o = mask_chunked_array_oop(o);
|
||||
push_depth(masked_o);
|
||||
TASKQUEUE_STATS_ONLY(++_arrays_chunked; ++_masked_pushes);
|
||||
} else {
|
||||
// we'll just push its contents
|
||||
new_obj->push_contents(this);
|
||||
}
|
||||
} else {
|
||||
// We lost, someone else "owns" this object
|
||||
guarantee(o->is_forwarded(), "Object must be forwarded if the cas failed.");
|
||||
|
||||
// Try to deallocate the space. If it was directly allocated we cannot
|
||||
// deallocate it, so we have to test. If the deallocation fails,
|
||||
// overwrite with a filler object.
|
||||
if (new_obj_is_tenured) {
|
||||
if (!_old_lab.unallocate_object((HeapWord*) new_obj, new_obj_size)) {
|
||||
CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size);
|
||||
}
|
||||
} else if (!_young_lab.unallocate_object((HeapWord*) new_obj, new_obj_size)) {
|
||||
CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size);
|
||||
}
|
||||
|
||||
// don't update this before the unallocation!
|
||||
new_obj = o->forwardee();
|
||||
}
|
||||
} else {
|
||||
assert(o->is_forwarded(), "Sanity");
|
||||
new_obj = o->forwardee();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// This code must come after the CAS test, or it will print incorrect
|
||||
// information.
|
||||
if (TraceScavenge) {
|
||||
gclog_or_tty->print_cr("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (" SIZE_FORMAT ")}",
|
||||
PSScavenge::should_scavenge(&new_obj) ? "copying" : "tenuring",
|
||||
new_obj->blueprint()->internal_name(), o, new_obj, new_obj->size());
|
||||
}
|
||||
#endif
|
||||
|
||||
return new_obj;
|
||||
}
|
||||
|
||||
template <class T> void PSPromotionManager::process_array_chunk_work(
|
||||
oop obj,
|
||||
int start, int end) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2012, 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
|
||||
@ -171,7 +171,7 @@ class PSPromotionManager : public CHeapObj {
|
||||
void set_old_gen_is_full(bool state) { _old_gen_is_full = state; }
|
||||
|
||||
// Promotion methods
|
||||
oop copy_to_survivor_space(oop o);
|
||||
template<bool promote_immediately> oop copy_to_survivor_space(oop o);
|
||||
oop oop_promotion_failed(oop obj, markOop obj_mark);
|
||||
|
||||
void reset();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2012, 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
|
||||
@ -61,6 +61,170 @@ inline void PSPromotionManager::claim_or_forward_depth(T* p) {
|
||||
claim_or_forward_internal_depth(p);
|
||||
}
|
||||
|
||||
//
|
||||
// This method is pretty bulky. It would be nice to split it up
|
||||
// into smaller submethods, but we need to be careful not to hurt
|
||||
// performance.
|
||||
//
|
||||
template<bool promote_immediately>
|
||||
oop PSPromotionManager::copy_to_survivor_space(oop o) {
|
||||
assert(PSScavenge::should_scavenge(&o), "Sanity");
|
||||
|
||||
oop new_obj = NULL;
|
||||
|
||||
// NOTE! We must be very careful with any methods that access the mark
|
||||
// in o. There may be multiple threads racing on it, and it may be forwarded
|
||||
// at any time. Do not use oop methods for accessing the mark!
|
||||
markOop test_mark = o->mark();
|
||||
|
||||
// The same test as "o->is_forwarded()"
|
||||
if (!test_mark->is_marked()) {
|
||||
bool new_obj_is_tenured = false;
|
||||
size_t new_obj_size = o->size();
|
||||
|
||||
if (!promote_immediately) {
|
||||
// Find the objects age, MT safe.
|
||||
int age = (test_mark->has_displaced_mark_helper() /* o->has_displaced_mark() */) ?
|
||||
test_mark->displaced_mark_helper()->age() : test_mark->age();
|
||||
|
||||
// Try allocating obj in to-space (unless too old)
|
||||
if (age < PSScavenge::tenuring_threshold()) {
|
||||
new_obj = (oop) _young_lab.allocate(new_obj_size);
|
||||
if (new_obj == NULL && !_young_gen_is_full) {
|
||||
// Do we allocate directly, or flush and refill?
|
||||
if (new_obj_size > (YoungPLABSize / 2)) {
|
||||
// Allocate this object directly
|
||||
new_obj = (oop)young_space()->cas_allocate(new_obj_size);
|
||||
} else {
|
||||
// Flush and fill
|
||||
_young_lab.flush();
|
||||
|
||||
HeapWord* lab_base = young_space()->cas_allocate(YoungPLABSize);
|
||||
if (lab_base != NULL) {
|
||||
_young_lab.initialize(MemRegion(lab_base, YoungPLABSize));
|
||||
// Try the young lab allocation again.
|
||||
new_obj = (oop) _young_lab.allocate(new_obj_size);
|
||||
} else {
|
||||
_young_gen_is_full = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise try allocating obj tenured
|
||||
if (new_obj == NULL) {
|
||||
#ifndef PRODUCT
|
||||
if (Universe::heap()->promotion_should_fail()) {
|
||||
return oop_promotion_failed(o, test_mark);
|
||||
}
|
||||
#endif // #ifndef PRODUCT
|
||||
|
||||
new_obj = (oop) _old_lab.allocate(new_obj_size);
|
||||
new_obj_is_tenured = true;
|
||||
|
||||
if (new_obj == NULL) {
|
||||
if (!_old_gen_is_full) {
|
||||
// Do we allocate directly, or flush and refill?
|
||||
if (new_obj_size > (OldPLABSize / 2)) {
|
||||
// Allocate this object directly
|
||||
new_obj = (oop)old_gen()->cas_allocate(new_obj_size);
|
||||
} else {
|
||||
// Flush and fill
|
||||
_old_lab.flush();
|
||||
|
||||
HeapWord* lab_base = old_gen()->cas_allocate(OldPLABSize);
|
||||
if(lab_base != NULL) {
|
||||
_old_lab.initialize(MemRegion(lab_base, OldPLABSize));
|
||||
// Try the old lab allocation again.
|
||||
new_obj = (oop) _old_lab.allocate(new_obj_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is the promotion failed test, and code handling.
|
||||
// The code belongs here for two reasons. It is slightly
|
||||
// different thatn the code below, and cannot share the
|
||||
// CAS testing code. Keeping the code here also minimizes
|
||||
// the impact on the common case fast path code.
|
||||
|
||||
if (new_obj == NULL) {
|
||||
_old_gen_is_full = true;
|
||||
return oop_promotion_failed(o, test_mark);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(new_obj != NULL, "allocation should have succeeded");
|
||||
|
||||
// Copy obj
|
||||
Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)new_obj, new_obj_size);
|
||||
|
||||
// Now we have to CAS in the header.
|
||||
if (o->cas_forward_to(new_obj, test_mark)) {
|
||||
// We won any races, we "own" this object.
|
||||
assert(new_obj == o->forwardee(), "Sanity");
|
||||
|
||||
// Increment age if obj still in new generation. Now that
|
||||
// we're dealing with a markOop that cannot change, it is
|
||||
// okay to use the non mt safe oop methods.
|
||||
if (!new_obj_is_tenured) {
|
||||
new_obj->incr_age();
|
||||
assert(young_space()->contains(new_obj), "Attempt to push non-promoted obj");
|
||||
}
|
||||
|
||||
// Do the size comparison first with new_obj_size, which we
|
||||
// already have. Hopefully, only a few objects are larger than
|
||||
// _min_array_size_for_chunking, and most of them will be arrays.
|
||||
// So, the is->objArray() test would be very infrequent.
|
||||
if (new_obj_size > _min_array_size_for_chunking &&
|
||||
new_obj->is_objArray() &&
|
||||
PSChunkLargeArrays) {
|
||||
// we'll chunk it
|
||||
oop* const masked_o = mask_chunked_array_oop(o);
|
||||
push_depth(masked_o);
|
||||
TASKQUEUE_STATS_ONLY(++_arrays_chunked; ++_masked_pushes);
|
||||
} else {
|
||||
// we'll just push its contents
|
||||
new_obj->push_contents(this);
|
||||
}
|
||||
} else {
|
||||
// We lost, someone else "owns" this object
|
||||
guarantee(o->is_forwarded(), "Object must be forwarded if the cas failed.");
|
||||
|
||||
// Try to deallocate the space. If it was directly allocated we cannot
|
||||
// deallocate it, so we have to test. If the deallocation fails,
|
||||
// overwrite with a filler object.
|
||||
if (new_obj_is_tenured) {
|
||||
if (!_old_lab.unallocate_object((HeapWord*) new_obj, new_obj_size)) {
|
||||
CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size);
|
||||
}
|
||||
} else if (!_young_lab.unallocate_object((HeapWord*) new_obj, new_obj_size)) {
|
||||
CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size);
|
||||
}
|
||||
|
||||
// don't update this before the unallocation!
|
||||
new_obj = o->forwardee();
|
||||
}
|
||||
} else {
|
||||
assert(o->is_forwarded(), "Sanity");
|
||||
new_obj = o->forwardee();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// This code must come after the CAS test, or it will print incorrect
|
||||
// information.
|
||||
if (TraceScavenge) {
|
||||
gclog_or_tty->print_cr("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (" SIZE_FORMAT ")}",
|
||||
PSScavenge::should_scavenge(&new_obj) ? "copying" : "tenuring",
|
||||
new_obj->blueprint()->internal_name(), o, new_obj, new_obj->size());
|
||||
}
|
||||
#endif
|
||||
|
||||
return new_obj;
|
||||
}
|
||||
|
||||
|
||||
inline void PSPromotionManager::process_popped_location_depth(StarTask p) {
|
||||
if (is_oop_masked(p)) {
|
||||
assert(PSChunkLargeArrays, "invariant");
|
||||
@ -69,9 +233,9 @@ inline void PSPromotionManager::process_popped_location_depth(StarTask p) {
|
||||
} else {
|
||||
if (p.is_narrow()) {
|
||||
assert(UseCompressedOops, "Error");
|
||||
PSScavenge::copy_and_push_safe_barrier(this, (narrowOop*)p);
|
||||
PSScavenge::copy_and_push_safe_barrier<narrowOop, /*promote_immediately=*/false>(this, p);
|
||||
} else {
|
||||
PSScavenge::copy_and_push_safe_barrier(this, (oop*)p);
|
||||
PSScavenge::copy_and_push_safe_barrier<oop, /*promote_immediately=*/false>(this, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
#include "gc_implementation/parallelScavenge/cardTableExtension.hpp"
|
||||
#include "gc_implementation/parallelScavenge/gcTaskManager.hpp"
|
||||
#include "gc_implementation/parallelScavenge/generationSizer.hpp"
|
||||
@ -100,7 +101,7 @@ public:
|
||||
|
||||
// Weak refs may be visited more than once.
|
||||
if (PSScavenge::should_scavenge(p, _to_space)) {
|
||||
PSScavenge::copy_and_push_safe_barrier(_promotion_manager, p);
|
||||
PSScavenge::copy_and_push_safe_barrier<T, /*promote_immediately=*/false>(_promotion_manager, p);
|
||||
}
|
||||
}
|
||||
virtual void do_oop(oop* p) { PSKeepAliveClosure::do_oop_work(p); }
|
||||
@ -214,36 +215,41 @@ void PSRefProcTaskExecutor::execute(EnqueueTask& task)
|
||||
//
|
||||
// Note that this method should only be called from the vm_thread while
|
||||
// at a safepoint!
|
||||
void PSScavenge::invoke() {
|
||||
bool PSScavenge::invoke() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint");
|
||||
assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread");
|
||||
assert(!Universe::heap()->is_gc_active(), "not reentrant");
|
||||
|
||||
ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
|
||||
ParallelScavengeHeap* const heap = (ParallelScavengeHeap*)Universe::heap();
|
||||
assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity");
|
||||
|
||||
PSAdaptiveSizePolicy* policy = heap->size_policy();
|
||||
IsGCActiveMark mark;
|
||||
|
||||
bool scavenge_was_done = PSScavenge::invoke_no_policy();
|
||||
const bool scavenge_done = PSScavenge::invoke_no_policy();
|
||||
const bool need_full_gc = !scavenge_done ||
|
||||
policy->should_full_GC(heap->old_gen()->free_in_bytes());
|
||||
bool full_gc_done = false;
|
||||
|
||||
PSGCAdaptivePolicyCounters* counters = heap->gc_policy_counters();
|
||||
if (UsePerfData)
|
||||
counters->update_full_follows_scavenge(0);
|
||||
if (!scavenge_was_done ||
|
||||
policy->should_full_GC(heap->old_gen()->free_in_bytes())) {
|
||||
if (UsePerfData)
|
||||
counters->update_full_follows_scavenge(full_follows_scavenge);
|
||||
if (UsePerfData) {
|
||||
PSGCAdaptivePolicyCounters* const counters = heap->gc_policy_counters();
|
||||
const int ffs_val = need_full_gc ? full_follows_scavenge : not_skipped;
|
||||
counters->update_full_follows_scavenge(ffs_val);
|
||||
}
|
||||
|
||||
if (need_full_gc) {
|
||||
GCCauseSetter gccs(heap, GCCause::_adaptive_size_policy);
|
||||
CollectorPolicy* cp = heap->collector_policy();
|
||||
const bool clear_all_softrefs = cp->should_clear_all_soft_refs();
|
||||
|
||||
if (UseParallelOldGC) {
|
||||
PSParallelCompact::invoke_no_policy(clear_all_softrefs);
|
||||
full_gc_done = PSParallelCompact::invoke_no_policy(clear_all_softrefs);
|
||||
} else {
|
||||
PSMarkSweep::invoke_no_policy(clear_all_softrefs);
|
||||
full_gc_done = PSMarkSweep::invoke_no_policy(clear_all_softrefs);
|
||||
}
|
||||
}
|
||||
|
||||
return full_gc_done;
|
||||
}
|
||||
|
||||
// This method contains no policy. You should probably
|
||||
@ -602,6 +608,8 @@ bool PSScavenge::invoke_no_policy() {
|
||||
|
||||
NOT_PRODUCT(reference_processor()->verify_no_references_recorded());
|
||||
|
||||
CodeCache::prune_scavenge_root_nmethods();
|
||||
|
||||
// Re-verify object start arrays
|
||||
if (VerifyObjectStartArray &&
|
||||
VerifyAfterGC) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2012, 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
|
||||
@ -117,10 +117,9 @@ class PSScavenge: AllStatic {
|
||||
// Called by parallelScavengeHeap to init the tenuring threshold
|
||||
static void initialize();
|
||||
|
||||
// Scavenge entry point
|
||||
static void invoke();
|
||||
// Return true is a collection was done. Return
|
||||
// false if the collection was skipped.
|
||||
// Scavenge entry point. This may invoke a full gc; return true if so.
|
||||
static bool invoke();
|
||||
// Return true if a collection was done; false otherwise.
|
||||
static bool invoke_no_policy();
|
||||
|
||||
// If an attempt to promote fails, this method is invoked
|
||||
@ -135,7 +134,8 @@ class PSScavenge: AllStatic {
|
||||
template <class T> static inline bool should_scavenge(T* p, MutableSpace* to_space);
|
||||
template <class T> static inline bool should_scavenge(T* p, bool check_to_space);
|
||||
|
||||
template <class T> inline static void copy_and_push_safe_barrier(PSPromotionManager* pm, T* p);
|
||||
template <class T, bool promote_immediately>
|
||||
inline static void copy_and_push_safe_barrier(PSPromotionManager* pm, T* p);
|
||||
|
||||
// Is an object in the young generation
|
||||
// This assumes that the HeapWord argument is in the heap,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2012, 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
|
||||
@ -28,6 +28,7 @@
|
||||
#include "gc_implementation/parallelScavenge/cardTableExtension.hpp"
|
||||
#include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp"
|
||||
#include "gc_implementation/parallelScavenge/psPromotionManager.hpp"
|
||||
#include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp"
|
||||
#include "gc_implementation/parallelScavenge/psScavenge.hpp"
|
||||
|
||||
inline void PSScavenge::save_to_space_top_before_gc() {
|
||||
@ -65,7 +66,7 @@ inline bool PSScavenge::should_scavenge(T* p, bool check_to_space) {
|
||||
// Attempt to "claim" oop at p via CAS, push the new obj if successful
|
||||
// This version tests the oop* to make sure it is within the heap before
|
||||
// attempting marking.
|
||||
template <class T>
|
||||
template <class T, bool promote_immediately>
|
||||
inline void PSScavenge::copy_and_push_safe_barrier(PSPromotionManager* pm,
|
||||
T* p) {
|
||||
assert(should_scavenge(p, true), "revisiting object?");
|
||||
@ -73,7 +74,7 @@ inline void PSScavenge::copy_and_push_safe_barrier(PSPromotionManager* pm,
|
||||
oop o = oopDesc::load_decode_heap_oop_not_null(p);
|
||||
oop new_obj = o->is_forwarded()
|
||||
? o->forwardee()
|
||||
: pm->copy_to_survivor_space(o);
|
||||
: pm->copy_to_survivor_space<promote_immediately>(o);
|
||||
oopDesc::encode_store_heap_oop_not_null(p, new_obj);
|
||||
|
||||
// We cannot mark without test, as some code passes us pointers
|
||||
@ -86,7 +87,8 @@ inline void PSScavenge::copy_and_push_safe_barrier(PSPromotionManager* pm,
|
||||
}
|
||||
}
|
||||
|
||||
class PSScavengeRootsClosure: public OopClosure {
|
||||
template<bool promote_immediately>
|
||||
class PSRootsClosure: public OopClosure {
|
||||
private:
|
||||
PSPromotionManager* _promotion_manager;
|
||||
|
||||
@ -94,13 +96,16 @@ class PSScavengeRootsClosure: public OopClosure {
|
||||
template <class T> void do_oop_work(T *p) {
|
||||
if (PSScavenge::should_scavenge(p)) {
|
||||
// We never card mark roots, maybe call a func without test?
|
||||
PSScavenge::copy_and_push_safe_barrier(_promotion_manager, p);
|
||||
PSScavenge::copy_and_push_safe_barrier<T, promote_immediately>(_promotion_manager, p);
|
||||
}
|
||||
}
|
||||
public:
|
||||
PSScavengeRootsClosure(PSPromotionManager* pm) : _promotion_manager(pm) { }
|
||||
void do_oop(oop* p) { PSScavengeRootsClosure::do_oop_work(p); }
|
||||
void do_oop(narrowOop* p) { PSScavengeRootsClosure::do_oop_work(p); }
|
||||
PSRootsClosure(PSPromotionManager* pm) : _promotion_manager(pm) { }
|
||||
void do_oop(oop* p) { PSRootsClosure::do_oop_work(p); }
|
||||
void do_oop(narrowOop* p) { PSRootsClosure::do_oop_work(p); }
|
||||
};
|
||||
|
||||
typedef PSRootsClosure</*promote_immediately=*/false> PSScavengeRootsClosure;
|
||||
typedef PSRootsClosure</*promote_immediately=*/true> PSPromoteRootsClosure;
|
||||
|
||||
#endif // SHARE_VM_GC_IMPLEMENTATION_PARALLELSCAVENGE_PSSCAVENGE_INLINE_HPP
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2012, 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
|
||||
@ -51,6 +51,7 @@ void ScavengeRootsTask::do_it(GCTaskManager* manager, uint which) {
|
||||
|
||||
PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which);
|
||||
PSScavengeRootsClosure roots_closure(pm);
|
||||
PSPromoteRootsClosure roots_to_old_closure(pm);
|
||||
|
||||
switch (_root_type) {
|
||||
case universe:
|
||||
@ -91,7 +92,7 @@ void ScavengeRootsTask::do_it(GCTaskManager* manager, uint which) {
|
||||
|
||||
case code_cache:
|
||||
{
|
||||
CodeBlobToOopClosure each_scavengable_code_blob(&roots_closure, /*do_marking=*/ true);
|
||||
CodeBlobToOopClosure each_scavengable_code_blob(&roots_to_old_closure, /*do_marking=*/ true);
|
||||
CodeCache::scavenge_root_nmethods_do(&each_scavengable_code_blob);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2012, 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
|
||||
@ -655,7 +655,12 @@ void DefNewGeneration::collect(bool full,
|
||||
from()->set_concurrent_iteration_safe_limit(from()->top());
|
||||
to()->set_concurrent_iteration_safe_limit(to()->top());
|
||||
SpecializationStats::print();
|
||||
update_time_of_last_gc(os::javaTimeMillis());
|
||||
|
||||
// We need to use a monotonically non-deccreasing time in ms
|
||||
// or we will see time-warp warnings and os::javaTimeMillis()
|
||||
// does not guarantee monotonicity.
|
||||
jlong now = os::javaTimeNanos() / NANOSECS_PER_MILLISEC;
|
||||
update_time_of_last_gc(now);
|
||||
}
|
||||
|
||||
class RemoveForwardPointerClosure: public ObjectClosure {
|
||||
|
||||
@ -176,7 +176,11 @@ void GenMarkSweep::invoke_at_safepoint(int level, ReferenceProcessor* rp,
|
||||
|
||||
// Update time of last gc for all generations we collected
|
||||
// (which curently is all the generations in the heap).
|
||||
gch->update_time_of_last_gc(os::javaTimeMillis());
|
||||
// We need to use a monotonically non-deccreasing time in ms
|
||||
// or we will see time-warp warnings and os::javaTimeMillis()
|
||||
// does not guarantee monotonicity.
|
||||
jlong now = os::javaTimeNanos() / NANOSECS_PER_MILLISEC;
|
||||
gch->update_time_of_last_gc(now);
|
||||
}
|
||||
|
||||
void GenMarkSweep::allocate_stacks() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user