8007036: G1: Too many old regions added to last mixed GC
Summary: Stop adding old regions to collection set when the remaining reclaimable bytes reaches, or goes below, G1HeapWastePercent. Changes were also reviewed by Vitaly Davidovich <vitalyd@gmail.com>.
Reviewed-by: brutisso
--- a/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp Mon Mar 04 13:01:24 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp Mon Mar 04 12:42:14 2013 -0800
@@ -146,43 +146,6 @@
verify();
}
-uint CollectionSetChooser::calc_min_old_cset_length() {
- // 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) G1MixedGCCountTarget;
- size_t result = region_num / gc_num;
- // emulate ceiling
- if (result * gc_num < region_num) {
- result += 1;
- }
- return (uint) result;
-}
-
-uint CollectionSetChooser::calc_max_old_cset_length() {
- // 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 (uint) result;
-}
void CollectionSetChooser::add_region(HeapRegion* hr) {
assert(!hr->isHumongous(),
--- a/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.hpp Mon Mar 04 13:01:24 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.hpp Mon Mar 04 12:42:14 2013 -0800
@@ -51,6 +51,8 @@
uint _curr_index;
// The number of candidate old regions added to the CSet chooser.
+ // Note: this is not updated when removing a region using
+ // remove_and_move_to_next() below.
uint _length;
// Keeps track of the start of the next array chunk to be claimed by
@@ -111,13 +113,8 @@
hr->live_bytes() < _region_live_threshold_bytes;
}
- // Calculate the minimum number of old regions we'll add to the CSet
- // during a mixed GC.
- uint calc_min_old_cset_length();
-
- // Calculate the maximum number of old regions we'll add to the CSet
- // during a mixed GC.
- uint calc_max_old_cset_length();
+ // Returns the number candidate old regions added
+ uint length() { return _length; }
// Serial version.
void add_region(HeapRegion *hr);
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Mon Mar 04 13:01:24 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Mon Mar 04 12:42:14 2013 -0800
@@ -1806,6 +1806,14 @@
}
#endif // !PRODUCT
+double G1CollectorPolicy::reclaimable_bytes_perc(size_t reclaimable_bytes) {
+ // Returns the given amount of reclaimable bytes (that represents
+ // the amount of reclaimable space still to be collected) as a
+ // percentage of the current heap capacity.
+ size_t capacity_bytes = _g1->capacity();
+ return (double) reclaimable_bytes * 100.0 / (double) capacity_bytes;
+}
+
bool G1CollectorPolicy::next_gc_should_be_mixed(const char* true_action_str,
const char* false_action_str) {
CollectionSetChooser* cset_chooser = _collectionSetChooser;
@@ -1815,19 +1823,21 @@
ergo_format_reason("candidate old regions not available"));
return false;
}
+
+ // Is the amount of uncollected reclaimable space above G1HeapWastePercent?
size_t reclaimable_bytes = cset_chooser->remaining_reclaimable_bytes();
- size_t capacity_bytes = _g1->capacity();
- double perc = (double) reclaimable_bytes * 100.0 / (double) capacity_bytes;
+ double reclaimable_perc = reclaimable_bytes_perc(reclaimable_bytes);
double threshold = (double) G1HeapWastePercent;
- if (perc < threshold) {
+ if (reclaimable_perc <= threshold) {
ergo_verbose4(ErgoMixedGCs,
false_action_str,
- ergo_format_reason("reclaimable percentage lower than threshold")
+ ergo_format_reason("reclaimable percentage not over threshold")
ergo_format_region("candidate old regions")
ergo_format_byte_perc("reclaimable")
ergo_format_perc("threshold"),
cset_chooser->remaining_regions(),
- reclaimable_bytes, perc, threshold);
+ reclaimable_bytes,
+ reclaimable_perc, threshold);
return false;
}
@@ -1838,10 +1848,50 @@
ergo_format_byte_perc("reclaimable")
ergo_format_perc("threshold"),
cset_chooser->remaining_regions(),
- reclaimable_bytes, perc, threshold);
+ reclaimable_bytes,
+ reclaimable_perc, threshold);
return true;
}
+uint G1CollectorPolicy::calc_min_old_cset_length() {
+ // 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) _collectionSetChooser->length();
+ const size_t gc_num = (size_t) MAX2(G1MixedGCCountTarget, (uintx) 1);
+ size_t result = region_num / gc_num;
+ // emulate ceiling
+ if (result * gc_num < region_num) {
+ result += 1;
+ }
+ return (uint) result;
+}
+
+uint G1CollectorPolicy::calc_max_old_cset_length() {
+ // 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 (uint) result;
+}
+
+
void G1CollectorPolicy::finalize_cset(double target_pause_time_ms) {
double young_start_time_sec = os::elapsedTime();
@@ -1855,7 +1905,7 @@
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;
+ double time_remaining_ms = MAX2(target_pause_time_ms - base_time_ms, 0.0);
ergo_verbose4(ErgoCSetConstruction | ErgoHigh,
"start choosing CSet",
@@ -1893,7 +1943,7 @@
_collection_set = _inc_cset_head;
_collection_set_bytes_used_before = _inc_cset_bytes_used_before;
- time_remaining_ms -= _inc_cset_predicted_elapsed_time_ms;
+ time_remaining_ms = MAX2(time_remaining_ms - _inc_cset_predicted_elapsed_time_ms, 0.0);
predicted_pause_time_ms += _inc_cset_predicted_elapsed_time_ms;
ergo_verbose3(ErgoCSetConstruction | ErgoHigh,
@@ -1917,8 +1967,8 @@
if (!gcs_are_young()) {
CollectionSetChooser* cset_chooser = _collectionSetChooser;
cset_chooser->verify();
- const uint min_old_cset_length = cset_chooser->calc_min_old_cset_length();
- const uint max_old_cset_length = cset_chooser->calc_max_old_cset_length();
+ const uint min_old_cset_length = calc_min_old_cset_length();
+ const uint max_old_cset_length = calc_max_old_cset_length();
uint expensive_region_num = 0;
bool check_time_remaining = adaptive_young_list_length();
@@ -1936,6 +1986,30 @@
break;
}
+
+ // Stop adding regions if the remaining reclaimable space is
+ // not above G1HeapWastePercent.
+ size_t reclaimable_bytes = cset_chooser->remaining_reclaimable_bytes();
+ double reclaimable_perc = reclaimable_bytes_perc(reclaimable_bytes);
+ double threshold = (double) G1HeapWastePercent;
+ if (reclaimable_perc <= threshold) {
+ // We've added enough old regions that the amount of uncollected
+ // reclaimable space is at or below the waste threshold. Stop
+ // adding old regions to the CSet.
+ ergo_verbose5(ErgoCSetConstruction,
+ "finish adding old regions to CSet",
+ ergo_format_reason("reclaimable percentage not over threshold")
+ ergo_format_region("old")
+ ergo_format_region("max")
+ ergo_format_byte_perc("reclaimable")
+ ergo_format_perc("threshold"),
+ old_cset_region_length(),
+ max_old_cset_length,
+ reclaimable_bytes,
+ reclaimable_perc, threshold);
+ break;
+ }
+
double predicted_time_ms = predict_region_elapsed_time_ms(hr, gcs_are_young());
if (check_time_remaining) {
if (predicted_time_ms > time_remaining_ms) {
@@ -1975,7 +2049,7 @@
}
// We will add this region to the CSet.
- time_remaining_ms -= predicted_time_ms;
+ time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0);
predicted_pause_time_ms += predicted_time_ms;
cset_chooser->remove_and_move_to_next(hr);
_g1->old_set_remove(hr);
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Mon Mar 04 13:01:24 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Mon Mar 04 12:42:14 2013 -0800
@@ -619,6 +619,18 @@
bool predict_will_fit(uint young_length, double base_time_ms,
uint base_free_regions, double target_pause_time_ms);
+ // Calculate the minimum number of old regions we'll add to the CSet
+ // during a mixed GC.
+ uint calc_min_old_cset_length();
+
+ // Calculate the maximum number of old regions we'll add to the CSet
+ // during a mixed GC.
+ uint calc_max_old_cset_length();
+
+ // Returns the given amount of uncollected reclaimable space
+ // as a percentage of the current heap capacity.
+ double reclaimable_bytes_perc(size_t reclaimable_bytes);
+
public:
G1CollectorPolicy();