From aea40d988339b3ef86bf75f9c5fc7e52dc1ed132 Mon Sep 17 00:00:00 2001 From: johnc Date: Mon, 4 Mar 2013 12:42:14 -0800 Subject: [PATCH] 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 . Reviewed-by: brutisso --- .../g1/collectionSetChooser.cpp | 37 ------- .../g1/collectionSetChooser.hpp | 11 +-- .../g1/g1CollectorPolicy.cpp | 96 ++++++++++++++++--- .../g1/g1CollectorPolicy.hpp | 12 +++ 4 files changed, 101 insertions(+), 55 deletions(-) diff --git a/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp b/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp index f4c6a7143..ed4b807fb 100644 --- a/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp +++ b/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp @@ -146,43 +146,6 @@ void CollectionSetChooser::sort_regions() { 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(), diff --git a/src/share/vm/gc_implementation/g1/collectionSetChooser.hpp b/src/share/vm/gc_implementation/g1/collectionSetChooser.hpp index 1a147b831..e9f6f8225 100644 --- a/src/share/vm/gc_implementation/g1/collectionSetChooser.hpp +++ b/src/share/vm/gc_implementation/g1/collectionSetChooser.hpp @@ -51,6 +51,8 @@ class CollectionSetChooser: public CHeapObj { 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 @@ public: 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); diff --git a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp index 4231e95f3..87acfec56 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp @@ -1806,6 +1806,14 @@ void G1CollectorPolicy::print_collection_set(HeapRegion* list_head, outputStream } #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 @@ bool G1CollectorPolicy::next_gc_should_be_mixed(const char* true_action_str, 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 @@ bool G1CollectorPolicy::next_gc_should_be_mixed(const char* true_action_str, 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 @@ void G1CollectorPolicy::finalize_cset(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; + 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 @@ void G1CollectorPolicy::finalize_cset(double target_pause_time_ms) { _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 @@ void G1CollectorPolicy::finalize_cset(double target_pause_time_ms) { 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 @@ void G1CollectorPolicy::finalize_cset(double target_pause_time_ms) { 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 @@ void G1CollectorPolicy::finalize_cset(double target_pause_time_ms) { } // 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); diff --git a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp index d32d1e66c..0c04167c8 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp @@ -619,6 +619,18 @@ private: 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(); -- GitLab