8137082: Factor out G1 prediction code from G1CollectorPolicy and clean up
authortschatzl
Thu, 15 Oct 2015 10:07:28 +0200
changeset 33214 5a00fba36171
parent 33211 966a1acb1243
child 33215 dba714a14246
8137082: Factor out G1 prediction code from G1CollectorPolicy and clean up Summary: Factor out G1 prediction code from G1CollectorPolicy into its own class, constify methods of G1CollectorPolicy and move more implementations to the cpp file. Reviewed-by: jmasa, sangheki, ecaspole, kbarrett
hotspot/src/share/vm/gc/g1/concurrentMark.cpp
hotspot/src/share/vm/gc/g1/g1CollectorPolicy.cpp
hotspot/src/share/vm/gc/g1/g1CollectorPolicy.hpp
hotspot/src/share/vm/gc/g1/g1CollectorState.hpp
hotspot/src/share/vm/gc/g1/g1Predictions.cpp
hotspot/src/share/vm/gc/g1/g1Predictions.hpp
hotspot/src/share/vm/gc/g1/survRateGroup.cpp
hotspot/src/share/vm/gc/g1/survRateGroup.hpp
hotspot/src/share/vm/prims/jni.cpp
--- a/hotspot/src/share/vm/gc/g1/concurrentMark.cpp	Thu Oct 15 00:42:15 2015 +0000
+++ b/hotspot/src/share/vm/gc/g1/concurrentMark.cpp	Thu Oct 15 10:07:28 2015 +0200
@@ -3731,8 +3731,7 @@
   // and do_marking_step() is not being called serially.
   bool do_stealing = do_termination && !is_serial;
 
-  double diff_prediction_ms =
-    g1_policy->get_new_prediction(&_marking_step_diffs_ms);
+  double diff_prediction_ms = _g1h->g1_policy()->predictor().get_new_prediction(&_marking_step_diffs_ms);
   _time_target_ms = time_target_ms - diff_prediction_ms;
 
   // set up the variables that are used in the work-based scheme to
--- a/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.cpp	Thu Oct 15 00:42:15 2015 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.cpp	Thu Oct 15 10:07:28 2015 +0200
@@ -80,6 +80,7 @@
 };
 
 G1CollectorPolicy::G1CollectorPolicy() :
+  _predictor(G1ConfidencePercent / 100.0),
   _parallel_gc_threads(ParallelGCThreads),
 
   _recent_gc_times_ms(new TruncatedSeq(NumPrevPausesForHeuristics)),
@@ -127,8 +128,6 @@
   _survivor_cset_region_length(0),
   _old_cset_region_length(0),
 
-  _sigma(G1ConfidencePercent / 100.0),
-
   _collection_set(NULL),
   _collection_set_bytes_used_before(0),
 
@@ -151,12 +150,12 @@
 
   _gc_overhead_perc(0.0) {
 
-  // SurvRateGroups below must be initialized after '_sigma' because they
-  // indirectly access '_sigma' through this object passed to their constructor.
+  // SurvRateGroups below must be initialized after the predictor because they
+  // indirectly use it through this object passed to their constructor.
   _short_lived_surv_rate_group =
-    new SurvRateGroup(this, "Short Lived", G1YoungSurvRateNumRegionsSummary);
+    new SurvRateGroup(&_predictor, "Short Lived", G1YoungSurvRateNumRegionsSummary);
   _survivor_surv_rate_group =
-    new SurvRateGroup(this, "Survivor", G1YoungSurvRateNumRegionsSummary);
+    new SurvRateGroup(&_predictor, "Survivor", G1YoungSurvRateNumRegionsSummary);
 
   // Set up the region size and associated fields. Given that the
   // policy is created before the heap, we have to set this up here,
@@ -289,6 +288,10 @@
   _collectionSetChooser = new CollectionSetChooser();
 }
 
+double G1CollectorPolicy::get_new_prediction(TruncatedSeq const* seq) const {
+  return _predictor.get_new_prediction(seq);
+}
+
 void G1CollectorPolicy::initialize_alignments() {
   _space_alignment = HeapRegion::GrainBytes;
   size_t card_table_alignment = GenRemSet::max_alignment_constraint();
@@ -316,8 +319,7 @@
   }
 }
 
-const G1CollectorState* G1CollectorPolicy::collector_state() const { return _g1->collector_state(); }
-G1CollectorState* G1CollectorPolicy::collector_state() { return _g1->collector_state(); }
+G1CollectorState* G1CollectorPolicy::collector_state() const { return _g1->collector_state(); }
 
 G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), _adaptive_size(true),
         _min_desired_young_length(0), _max_desired_young_length(0) {
@@ -428,8 +430,8 @@
     _young_list_fixed_length = _young_gen_sizer->min_desired_young_length();
   }
   _free_regions_at_end_of_collection = _g1->num_free_regions();
+
   update_young_list_target_length();
-
   // We may immediately start allocating regions and placing them on the
   // collection set list. Initialize the per-collection set info
   start_incremental_cset_building();
@@ -460,9 +462,8 @@
     return false;
   }
 
-  size_t free_bytes =
-                   (base_free_regions - young_length) * HeapRegion::GrainBytes;
-  if ((2.0 * sigma()) * (double) bytes_to_copy > (double) free_bytes) {
+  size_t free_bytes = (base_free_regions - young_length) * HeapRegion::GrainBytes;
+  if ((2.0 /* magic */ * _predictor.sigma()) * bytes_to_copy > free_bytes) {
     // end condition 3: out-of-space (conservatively!)
     return false;
   }
@@ -1269,7 +1270,7 @@
     cg1r->set_red_zone(g * k_gr);
     cg1r->reinitialize_threads();
 
-    int processing_threshold_delta = MAX2((int)(cg1r->green_zone() * sigma()), 1);
+    int processing_threshold_delta = MAX2((int)(cg1r->green_zone() * _predictor.sigma()), 1);
     int processing_threshold = MIN2(cg1r->green_zone() + processing_threshold_delta,
                                     cg1r->yellow_zone());
     // Change the barrier params
@@ -1286,17 +1287,125 @@
   dcqs.notify_if_necessary();
 }
 
-double
-G1CollectorPolicy::predict_base_elapsed_time_ms(size_t pending_cards,
-                                                size_t scanned_cards) const {
+size_t G1CollectorPolicy::predict_rs_length_diff() const {
+  return (size_t) get_new_prediction(_rs_length_diff_seq);
+}
+
+double G1CollectorPolicy::predict_alloc_rate_ms() const {
+  return get_new_prediction(_alloc_rate_ms_seq);
+}
+
+double G1CollectorPolicy::predict_cost_per_card_ms() const {
+  return get_new_prediction(_cost_per_card_ms_seq);
+}
+
+double G1CollectorPolicy::predict_scan_hcc_ms() const {
+  return get_new_prediction(_cost_scan_hcc_seq);
+}
+
+double G1CollectorPolicy::predict_rs_update_time_ms(size_t pending_cards) const {
+  return pending_cards * predict_cost_per_card_ms() + predict_scan_hcc_ms();
+}
+
+double G1CollectorPolicy::predict_young_cards_per_entry_ratio() const {
+  return get_new_prediction(_young_cards_per_entry_ratio_seq);
+}
+
+double G1CollectorPolicy::predict_mixed_cards_per_entry_ratio() const {
+  if (_mixed_cards_per_entry_ratio_seq->num() < 2) {
+    return predict_young_cards_per_entry_ratio();
+  } else {
+    return get_new_prediction(_mixed_cards_per_entry_ratio_seq);
+  }
+}
+
+size_t G1CollectorPolicy::predict_young_card_num(size_t rs_length) const {
+  return (size_t) (rs_length * predict_young_cards_per_entry_ratio());
+}
+
+size_t G1CollectorPolicy::predict_non_young_card_num(size_t rs_length) const {
+  return (size_t)(rs_length * predict_mixed_cards_per_entry_ratio());
+}
+
+double G1CollectorPolicy::predict_rs_scan_time_ms(size_t card_num) const {
+  if (collector_state()->gcs_are_young()) {
+    return card_num * get_new_prediction(_cost_per_entry_ms_seq);
+  } else {
+    return predict_mixed_rs_scan_time_ms(card_num);
+  }
+}
+
+double G1CollectorPolicy::predict_mixed_rs_scan_time_ms(size_t card_num) const {
+  if (_mixed_cost_per_entry_ms_seq->num() < 3) {
+    return card_num * get_new_prediction(_cost_per_entry_ms_seq);
+  } else {
+    return card_num * get_new_prediction(_mixed_cost_per_entry_ms_seq);
+  }
+}
+
+double G1CollectorPolicy::predict_object_copy_time_ms_during_cm(size_t bytes_to_copy) const {
+  if (_cost_per_byte_ms_during_cm_seq->num() < 3) {
+    return (1.1 * bytes_to_copy) * get_new_prediction(_cost_per_byte_ms_seq);
+  } else {
+    return bytes_to_copy * get_new_prediction(_cost_per_byte_ms_during_cm_seq);
+  }
+}
+
+double G1CollectorPolicy::predict_object_copy_time_ms(size_t bytes_to_copy) const {
+  if (collector_state()->during_concurrent_mark()) {
+    return predict_object_copy_time_ms_during_cm(bytes_to_copy);
+  } else {
+    return bytes_to_copy * get_new_prediction(_cost_per_byte_ms_seq);
+  }
+}
+
+double G1CollectorPolicy::predict_constant_other_time_ms() const {
+  return get_new_prediction(_constant_other_time_ms_seq);
+}
+
+double G1CollectorPolicy::predict_young_other_time_ms(size_t young_num) const {
+  return young_num * get_new_prediction(_young_other_cost_per_region_ms_seq);
+}
+
+double G1CollectorPolicy::predict_non_young_other_time_ms(size_t non_young_num) const {
+  return non_young_num * get_new_prediction(_non_young_other_cost_per_region_ms_seq);
+}
+
+double G1CollectorPolicy::predict_remark_time_ms() const {
+  return get_new_prediction(_concurrent_mark_remark_times_ms);
+}
+
+double G1CollectorPolicy::predict_cleanup_time_ms() const {
+  return get_new_prediction(_concurrent_mark_cleanup_times_ms);
+}
+
+double G1CollectorPolicy::predict_yg_surv_rate(int age, SurvRateGroup* surv_rate_group) const {
+  TruncatedSeq* seq = surv_rate_group->get_seq(age);
+  guarantee(seq->num() > 0, "There should be some young gen survivor samples available. Tried to access with age %d", age);
+  double pred = get_new_prediction(seq);
+  if (pred > 1.0) {
+    pred = 1.0;
+  }
+  return pred;
+}
+
+double G1CollectorPolicy::predict_yg_surv_rate(int age) const {
+  return predict_yg_surv_rate(age, _short_lived_surv_rate_group);
+}
+
+double G1CollectorPolicy::accum_yg_surv_rate_pred(int age) const {
+  return _short_lived_surv_rate_group->accum_surv_rate_pred(age);
+}
+
+double G1CollectorPolicy::predict_base_elapsed_time_ms(size_t pending_cards,
+                                                       size_t scanned_cards) const {
   return
     predict_rs_update_time_ms(pending_cards) +
     predict_rs_scan_time_ms(scanned_cards) +
     predict_constant_other_time_ms();
 }
 
-double
-G1CollectorPolicy::predict_base_elapsed_time_ms(size_t pending_cards) const {
+double G1CollectorPolicy::predict_base_elapsed_time_ms(size_t pending_cards) const {
   size_t rs_length = predict_rs_length_diff();
   size_t card_num;
   if (collector_state()->gcs_are_young()) {
@@ -1315,14 +1424,13 @@
     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);
+    bytes_to_copy = (size_t) (hr->used() * yg_surv_rate);
   }
   return bytes_to_copy;
 }
 
-double
-G1CollectorPolicy::predict_region_elapsed_time_ms(HeapRegion* hr,
-                                                  bool for_young_gc) const {
+double G1CollectorPolicy::predict_region_elapsed_time_ms(HeapRegion* hr,
+                                                         bool for_young_gc) const {
   size_t rs_length = hr->rem_set()->occupied();
   size_t card_num;
 
@@ -1349,9 +1457,8 @@
   return region_elapsed_time_ms;
 }
 
-void
-G1CollectorPolicy::init_cset_region_lengths(uint eden_cset_region_length,
-                                            uint survivor_cset_region_length) {
+void G1CollectorPolicy::init_cset_region_lengths(uint eden_cset_region_length,
+                                                 uint survivor_cset_region_length) {
   _eden_cset_region_length     = eden_cset_region_length;
   _survivor_cset_region_length = survivor_cset_region_length;
   _old_cset_region_length      = 0;
--- a/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.hpp	Thu Oct 15 00:42:15 2015 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.hpp	Thu Oct 15 10:07:28 2015 +0200
@@ -29,6 +29,7 @@
 #include "gc/g1/g1CollectorState.hpp"
 #include "gc/g1/g1InCSetState.hpp"
 #include "gc/g1/g1MMUTracker.hpp"
+#include "gc/g1/g1Predictions.hpp"
 #include "gc/shared/collectorPolicy.hpp"
 
 // A G1CollectorPolicy makes policy decisions that determine the
@@ -161,7 +162,11 @@
 };
 
 class G1CollectorPolicy: public CollectorPolicy {
-private:
+ private:
+  G1Predictions _predictor;
+
+  double get_new_prediction(TruncatedSeq const* seq) const;
+
   // either equal to the number of parallel threads, if ParallelGCThreads
   // has been set, or 1 otherwise
   int _parallel_gc_threads;
@@ -169,10 +174,6 @@
   // The number of GC threads currently active.
   uintx _no_of_gc_threads;
 
-  enum SomePrivateConstants {
-    NumPrevPausesForHeuristics = 10
-  };
-
   G1MMUTracker* _mmu_tracker;
 
   void initialize_alignments();
@@ -211,7 +212,8 @@
   uint   _reserve_regions;
 
   enum PredictionConstants {
-    TruncatedSeqLength = 10
+    TruncatedSeqLength = 10,
+    NumPrevPausesForHeuristics = 10
   };
 
   TruncatedSeq* _alloc_rate_ms_seq;
@@ -251,25 +253,9 @@
 
   size_t _recorded_rs_lengths;
   size_t _max_rs_lengths;
-  double _sigma;
 
   size_t _rs_lengths_prediction;
 
-  double sigma() const { 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
-  // of samples.  5 or more samples yields one; fewer scales linearly from
-  // 2.0 at 1 sample to 1.0 at 5.
-  double confidence_factor(int samples) const {
-    if (samples > 4) return 1.0;
-    else return  1.0 + sigma() * ((double)(5 - samples))/2.0;
-  }
-
-  double get_new_neg_prediction(TruncatedSeq* seq) {
-    return seq->davg() - sigma() * seq->dsd();
-  }
-
 #ifndef PRODUCT
   bool verify_young_ages(HeapRegion* head, SurvRateGroup *surv_rate_group);
 #endif // PRODUCT
@@ -286,6 +272,8 @@
   size_t _pending_cards;
 
 public:
+  G1Predictions& predictor() { return _predictor; }
+
   // Accessors
 
   void set_region_eden(HeapRegion* hr, int young_index_in_cset) {
@@ -304,106 +292,41 @@
   bool verify_young_ages();
 #endif // PRODUCT
 
-  double get_new_prediction(TruncatedSeq* seq) const {
-    return MAX2(seq->davg() + sigma() * seq->dsd(),
-                seq->davg() * confidence_factor(seq->num()));
-  }
-
   void record_max_rs_lengths(size_t rs_lengths) {
     _max_rs_lengths = rs_lengths;
   }
 
-  size_t predict_rs_length_diff() const {
-    return (size_t) get_new_prediction(_rs_length_diff_seq);
-  }
+  size_t predict_rs_length_diff() const;
 
-  double predict_alloc_rate_ms() const {
-    return get_new_prediction(_alloc_rate_ms_seq);
-  }
+  double predict_alloc_rate_ms() const;
 
-  double predict_cost_per_card_ms() const {
-    return get_new_prediction(_cost_per_card_ms_seq);
-  }
+  double predict_cost_per_card_ms() const;
 
-  double predict_scan_hcc_ms() const {
-    return get_new_prediction(_cost_scan_hcc_seq);
-  }
-
-  double predict_rs_update_time_ms(size_t pending_cards) const {
-    return (double) pending_cards * predict_cost_per_card_ms() + predict_scan_hcc_ms();
-  }
+  double predict_scan_hcc_ms() const;
 
-  double predict_young_cards_per_entry_ratio() const {
-    return get_new_prediction(_young_cards_per_entry_ratio_seq);
-  }
+  double predict_rs_update_time_ms(size_t pending_cards) const;
+
+  double predict_young_cards_per_entry_ratio() const;
 
-  double predict_mixed_cards_per_entry_ratio() const {
-    if (_mixed_cards_per_entry_ratio_seq->num() < 2) {
-      return predict_young_cards_per_entry_ratio();
-    } else {
-      return get_new_prediction(_mixed_cards_per_entry_ratio_seq);
-    }
-  }
+  double predict_mixed_cards_per_entry_ratio() const;
 
-  size_t predict_young_card_num(size_t rs_length) const {
-    return (size_t) ((double) rs_length *
-                     predict_young_cards_per_entry_ratio());
-  }
-
-  size_t predict_non_young_card_num(size_t rs_length) const {
-    return (size_t) ((double) rs_length *
-                     predict_mixed_cards_per_entry_ratio());
-  }
+  size_t predict_young_card_num(size_t rs_length) const;
 
-  double predict_rs_scan_time_ms(size_t card_num) const {
-    if (collector_state()->gcs_are_young()) {
-      return (double) card_num * get_new_prediction(_cost_per_entry_ms_seq);
-    } else {
-      return predict_mixed_rs_scan_time_ms(card_num);
-    }
-  }
+  size_t predict_non_young_card_num(size_t rs_length) const;
+
+  double predict_rs_scan_time_ms(size_t card_num) const;
 
-  double predict_mixed_rs_scan_time_ms(size_t card_num) const {
-    if (_mixed_cost_per_entry_ms_seq->num() < 3) {
-      return (double) card_num * get_new_prediction(_cost_per_entry_ms_seq);
-    } else {
-      return (double) (card_num *
-                       get_new_prediction(_mixed_cost_per_entry_ms_seq));
-    }
-  }
+  double predict_mixed_rs_scan_time_ms(size_t card_num) const;
+
+  double predict_object_copy_time_ms_during_cm(size_t bytes_to_copy) const;
 
-  double predict_object_copy_time_ms_during_cm(size_t bytes_to_copy) const {
-    if (_cost_per_byte_ms_during_cm_seq->num() < 3) {
-      return (1.1 * (double) bytes_to_copy) *
-              get_new_prediction(_cost_per_byte_ms_seq);
-    } else {
-      return (double) bytes_to_copy *
-             get_new_prediction(_cost_per_byte_ms_during_cm_seq);
-    }
-  }
+  double predict_object_copy_time_ms(size_t bytes_to_copy) const;
+
+  double predict_constant_other_time_ms() const;
 
-  double predict_object_copy_time_ms(size_t bytes_to_copy) const {
-    if (collector_state()->during_concurrent_mark()) {
-      return predict_object_copy_time_ms_during_cm(bytes_to_copy);
-    } else {
-      return (double) bytes_to_copy *
-              get_new_prediction(_cost_per_byte_ms_seq);
-    }
-  }
+  double predict_young_other_time_ms(size_t young_num) const;
 
-  double predict_constant_other_time_ms() const {
-    return get_new_prediction(_constant_other_time_ms_seq);
-  }
-
-  double predict_young_other_time_ms(size_t young_num) const {
-    return (double) young_num *
-           get_new_prediction(_young_other_cost_per_region_ms_seq);
-  }
-
-  double predict_non_young_other_time_ms(size_t non_young_num) const {
-    return (double) non_young_num *
-           get_new_prediction(_non_young_other_cost_per_region_ms_seq);
-  }
+  double predict_non_young_other_time_ms(size_t non_young_num) const;
 
   double predict_base_elapsed_time_ms(size_t pending_cards) const;
   double predict_base_elapsed_time_ms(size_t pending_cards,
@@ -439,34 +362,17 @@
     return _mmu_tracker->max_gc_time() * 1000.0;
   }
 
-  double predict_remark_time_ms() const {
-    return get_new_prediction(_concurrent_mark_remark_times_ms);
-  }
+  double predict_remark_time_ms() const;
 
-  double predict_cleanup_time_ms() const {
-    return get_new_prediction(_concurrent_mark_cleanup_times_ms);
-  }
+  double predict_cleanup_time_ms() const;
 
   // Returns an estimate of the survival rate of the region at yg-age
   // "yg_age".
-  double predict_yg_surv_rate(int age, SurvRateGroup* surv_rate_group) const {
-    TruncatedSeq* seq = surv_rate_group->get_seq(age);
-    if (seq->num() == 0)
-      gclog_or_tty->print("BARF! age is %d", age);
-    guarantee( seq->num() > 0, "invariant" );
-    double pred = get_new_prediction(seq);
-    if (pred > 1.0)
-      pred = 1.0;
-    return pred;
-  }
+  double predict_yg_surv_rate(int age, SurvRateGroup* surv_rate_group) const;
 
-  double predict_yg_surv_rate(int age) const {
-    return predict_yg_surv_rate(age, _short_lived_surv_rate_group);
-  }
+  double predict_yg_surv_rate(int age) const;
 
-  double accum_yg_surv_rate_pred(int age) const {
-    return _short_lived_surv_rate_group->accum_surv_rate_pred(age);
-  }
+  double accum_yg_surv_rate_pred(int age) const;
 
 private:
   // Statistics kept per GC stoppage, pause or full.
@@ -613,8 +519,7 @@
 
   virtual G1CollectorPolicy* as_g1_policy() { return this; }
 
-  const G1CollectorState* collector_state() const;
-  G1CollectorState* collector_state();
+  G1CollectorState* collector_state() const;
 
   G1GCPhaseTimes* phase_times() const { return _phase_times; }
 
@@ -888,15 +793,4 @@
   virtual void post_heap_initialize();
 };
 
-// This should move to some place more general...
-
-// If we have "n" measurements, and we've kept track of their "sum" and the
-// "sum_of_squares" of the measurements, this returns the variance of the
-// sequence.
-inline double variance(int n, double sum_of_squares, double sum) {
-  double n_d = (double)n;
-  double avg = sum/n_d;
-  return (sum_of_squares - 2.0 * avg * sum + n_d * avg * avg) / n_d;
-}
-
 #endif // SHARE_VM_GC_G1_G1COLLECTORPOLICY_HPP
--- a/hotspot/src/share/vm/gc/g1/g1CollectorState.hpp	Thu Oct 15 00:42:15 2015 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1CollectorState.hpp	Thu Oct 15 10:07:28 2015 +0200
@@ -125,7 +125,7 @@
     return (_last_young_gc && !_in_marking_window);
   }
 
-  G1YCType yc_type() {
+  G1YCType yc_type() const {
     if (during_initial_mark_pause()) {
       return InitialMark;
     } else if (mark_in_progress()) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1Predictions.cpp	Thu Oct 15 10:07:28 2015 +0200
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc/g1/g1Predictions.hpp"
+
+#ifndef PRODUCT
+
+void G1Predictions::test() {
+  double const epsilon = 1e-6;
+  {
+    // Some basic formula tests with confidence = 0.0
+    G1Predictions predictor(0.0);
+    TruncatedSeq s;
+
+    double p0 = predictor.get_new_prediction(&s);
+    assert(p0 < epsilon, "Initial prediction of empty sequence must be 0.0 but is %f", p0);
+
+    s.add(5.0);
+    double p1 = predictor.get_new_prediction(&s);
+    assert(fabs(p1 - 5.0) < epsilon, "Prediction should be 5.0 but is %f", p1);
+    for (int i = 0; i < 40; i++) {
+      s.add(5.0);
+    }
+    double p2 = predictor.get_new_prediction(&s);
+    assert(fabs(p2 - 5.0) < epsilon, "Prediction should be 5.0 but is %f", p1);
+  }
+
+  {
+    // The following tests checks that the initial predictions are based on the
+    // average of the sequence and not on the stddev (which is 0).
+    G1Predictions predictor(0.5);
+    TruncatedSeq s;
+
+    s.add(1.0);
+    double p1 = predictor.get_new_prediction(&s);
+    assert(p1 > 1.0, "First prediction must be larger than average, but avg is %f and prediction %f", s.davg(), p1);
+    s.add(1.0);
+    double p2 = predictor.get_new_prediction(&s);
+    assert(p2 < p1, "First prediction must be larger than second, but they are %f %f", p1, p2);
+    s.add(1.0);
+    double p3 = predictor.get_new_prediction(&s);
+    assert(p3 < p2, "Second prediction must be larger than third, but they are %f %f", p2, p3);
+    s.add(1.0);
+    s.add(1.0); // Five elements are now in the sequence.
+    double p5 = predictor.get_new_prediction(&s);
+    assert(p5 < p3, "Fifth prediction must be smaller than third, but they are %f %f", p3, p5);
+    assert(fabs(p5 - 1.0) < epsilon, "Prediction must be 1.0+epsilon, but is %f", p5);
+  }
+
+  {
+    // The following tests checks that initially prediction based on the average is
+    // used, that gets overridden by the stddev prediction at the end.
+    G1Predictions predictor(0.5);
+    TruncatedSeq s;
+
+    s.add(0.5);
+    double p1 = predictor.get_new_prediction(&s);
+    assert(p1 > 0.5, "First prediction must be larger than average, but avg is %f and prediction %f", s.davg(), p1);
+    s.add(0.2);
+    double p2 = predictor.get_new_prediction(&s);
+    assert(p2 < p1, "First prediction must be larger than second, but they are %f %f", p1, p2);
+    s.add(0.5);
+    double p3 = predictor.get_new_prediction(&s);
+    assert(p3 < p2, "Second prediction must be larger than third, but they are %f %f", p2, p3);
+    s.add(0.2);
+    s.add(2.0);
+    double p5 = predictor.get_new_prediction(&s);
+    assert(p5 > p3, "Fifth prediction must be bigger than third, but they are %f %f", p3, p5);
+  }
+}
+
+void TestPredictions_test() {
+  G1Predictions::test();
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc/g1/g1Predictions.hpp	Thu Oct 15 10:07:28 2015 +0200
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_GC_G1_G1PREDICTIONS_HPP
+#define SHARE_VM_GC_G1_G1PREDICTIONS_HPP
+
+#include "memory/allocation.inline.hpp"
+#include "utilities/numberSeq.hpp"
+
+// Utility class containing various helper methods for prediction.
+class G1Predictions VALUE_OBJ_CLASS_SPEC {
+ private:
+  double _sigma;
+
+  // This function is used to estimate the stddev of sample sets. There is some
+  // special consideration of small sample sets: the actual stddev for them is
+  // not very useful, so we calculate some value based on the sample average.
+  // Five or more samples yields zero (at that point we use the stddev); fewer
+  // scale the sample set average linearly from two times the average to 0.5 times
+  // it.
+  double stddev_estimate(TruncatedSeq const* seq) const {
+    double estimate = seq->dsd();
+    int const samples = seq->num();
+    if (samples < 5) {
+      estimate = MAX2(seq->davg() * (5 - samples) / 2.0, estimate);
+    }
+    return estimate;
+  }
+ public:
+  G1Predictions(double sigma) : _sigma(sigma) {
+    assert(sigma >= 0.0, "Confidence must be larger than or equal to zero");
+  }
+
+  // Confidence factor.
+  double sigma() const { return _sigma; }
+
+  double get_new_prediction(TruncatedSeq const* seq) const {
+    return seq->davg() + _sigma * stddev_estimate(seq);
+  }
+
+#ifndef PRODUCT
+  static void test();
+#endif
+};
+
+#endif // SHARE_VM_GC_G1_G1PREDICTIONS_HPP
--- a/hotspot/src/share/vm/gc/g1/survRateGroup.cpp	Thu Oct 15 00:42:15 2015 +0000
+++ b/hotspot/src/share/vm/gc/g1/survRateGroup.cpp	Thu Oct 15 10:07:28 2015 +0200
@@ -24,15 +24,15 @@
 
 #include "precompiled.hpp"
 #include "gc/g1/g1CollectedHeap.inline.hpp"
-#include "gc/g1/g1CollectorPolicy.hpp"
+#include "gc/g1/g1Predictions.hpp"
 #include "gc/g1/heapRegion.hpp"
 #include "gc/g1/survRateGroup.hpp"
 #include "memory/allocation.hpp"
 
-SurvRateGroup::SurvRateGroup(G1CollectorPolicy* g1p,
+SurvRateGroup::SurvRateGroup(G1Predictions* predictor,
                              const char* name,
                              size_t summary_surv_rates_len) :
-    _g1p(g1p), _name(name),
+    _predictor(predictor), _name(name),
     _summary_surv_rates_len(summary_surv_rates_len),
     _summary_surv_rates_max_len(0),
     _summary_surv_rates(NULL),
@@ -52,6 +52,10 @@
   start_adding_regions();
 }
 
+double SurvRateGroup::get_new_prediction(TruncatedSeq const* seq) const {
+  return _predictor->get_new_prediction(seq);
+}
+
 void SurvRateGroup::reset() {
   _all_regions_allocated = 0;
   _setup_seq_num         = 0;
@@ -127,7 +131,7 @@
   double ret = _accum_surv_rate;
   if (adjustment > 0) {
     TruncatedSeq* seq = get_seq(_region_num+1);
-    double surv_rate = _g1p->get_new_prediction(seq);
+    double surv_rate = get_new_prediction(seq);
     ret += surv_rate;
   }
 
@@ -137,7 +141,7 @@
 int
 SurvRateGroup::next_age_index() {
   TruncatedSeq* seq = get_seq(_region_num);
-  double surv_rate = _g1p->get_new_prediction(seq);
+  double surv_rate = get_new_prediction(seq);
   _accum_surv_rate += surv_rate;
 
   ++_region_num;
@@ -175,7 +179,7 @@
   double accum = 0.0;
   double pred = 0.0;
   for (size_t i = 0; i < _stats_arrays_length; ++i) {
-    pred = _g1p->get_new_prediction(_surv_rate_pred[i]);
+    pred = get_new_prediction(_surv_rate_pred[i]);
     if (pred > 1.0) pred = 1.0;
     accum += pred;
     _accum_surv_rate_pred[i] = accum;
@@ -192,7 +196,7 @@
   for (size_t i = 0; i < _region_num; ++i) {
     gclog_or_tty->print_cr("    age " SIZE_FORMAT_W(4) "   surv rate %6.2lf %%   pred %6.2lf %%",
                   i, _surv_rate[i] * 100.0,
-                  _g1p->get_new_prediction(_surv_rate_pred[i]) * 100.0);
+                  _predictor->get_new_prediction(_surv_rate_pred[i]) * 100.0);
   }
 }
 
--- a/hotspot/src/share/vm/gc/g1/survRateGroup.hpp	Thu Oct 15 00:42:15 2015 +0000
+++ b/hotspot/src/share/vm/gc/g1/survRateGroup.hpp	Thu Oct 15 10:07:28 2015 +0200
@@ -27,11 +27,14 @@
 
 #include "utilities/numberSeq.hpp"
 
-class G1CollectorPolicy;
+class G1Predictions;
 
 class SurvRateGroup : public CHeapObj<mtGC> {
 private:
-  G1CollectorPolicy* _g1p;
+  G1Predictions* _predictor;
+
+  double get_new_prediction(TruncatedSeq const* seq) const;
+
   const char* _name;
 
   size_t  _stats_arrays_length;
@@ -49,7 +52,7 @@
   size_t _setup_seq_num;
 
 public:
-  SurvRateGroup(G1CollectorPolicy* g1p,
+  SurvRateGroup(G1Predictions* predictor,
                 const char* name,
                 size_t summary_surv_rates_len);
   void reset();
@@ -60,7 +63,7 @@
   const char* name() { return _name; }
 
   size_t region_num() { return _region_num; }
-  double accum_surv_rate_pred(int age) {
+  double accum_surv_rate_pred(int age) const {
     assert(age >= 0, "must be");
     if ((size_t)age < _stats_arrays_length)
       return _accum_surv_rate_pred[age];
@@ -72,7 +75,7 @@
 
   double accum_surv_rate(size_t adjustment);
 
-  TruncatedSeq* get_seq(size_t age) {
+  TruncatedSeq* get_seq(size_t age) const {
     if (age >= _setup_seq_num) {
       guarantee( _setup_seq_num > 0, "invariant" );
       age = _setup_seq_num-1;
--- a/hotspot/src/share/vm/prims/jni.cpp	Thu Oct 15 00:42:15 2015 +0000
+++ b/hotspot/src/share/vm/prims/jni.cpp	Thu Oct 15 10:07:28 2015 +0200
@@ -3870,6 +3870,7 @@
 void TestCodeCacheRemSet_test();
 void FreeRegionList_test();
 void test_memset_with_concurrent_readers();
+void TestPredictions_test();
 #endif
 
 void execute_internal_vm_tests() {
@@ -3912,6 +3913,7 @@
       run_unit_test(FreeRegionList_test());
     }
     run_unit_test(test_memset_with_concurrent_readers());
+    run_unit_test(TestPredictions_test());
 #endif
     tty->print_cr("All internal VM tests passed");
   }