8028391: Make the Min/MaxHeapFreeRatio flags manageable
authorjwilhelm
Wed, 29 Jan 2014 23:17:05 +0100
changeset 22555 ea32f6c51d08
parent 22554 a19b9cf9ffa8
child 22556 a17351e8c2c2
child 22760 8469dee00b80
8028391: Make the Min/MaxHeapFreeRatio flags manageable Summary: Made the flags Min- and MaxHeapFreeRatio manageable, and implemented support for these flags in ParallalGC. Reviewed-by: sla, mgerdin, brutisso
hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp
hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp
hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp
hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp
hotspot/src/share/vm/prims/jni.cpp
hotspot/src/share/vm/runtime/arguments.cpp
hotspot/src/share/vm/runtime/arguments.hpp
hotspot/src/share/vm/runtime/globals.hpp
hotspot/src/share/vm/services/attachListener.cpp
hotspot/src/share/vm/services/management.cpp
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Wed Jan 29 23:17:05 2014 +0100
@@ -1575,8 +1575,6 @@
 void
 G1CollectedHeap::
 resize_if_necessary_after_full_collection(size_t word_size) {
-  assert(MinHeapFreeRatio <= MaxHeapFreeRatio, "sanity check");
-
   // Include the current allocation, if any, and bytes that will be
   // pre-allocated to support collections, as "used".
   const size_t used_after_gc = used();
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp	Wed Jan 29 23:17:05 2014 +0100
@@ -23,6 +23,7 @@
  */
 
 #include "precompiled.hpp"
+#include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp"
 #include "gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp"
 #include "gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.hpp"
 #include "gc_implementation/parallelScavenge/psScavenge.hpp"
@@ -76,6 +77,38 @@
   _old_gen_policy_is_ready = false;
 }
 
+size_t PSAdaptiveSizePolicy::calculate_free_based_on_live(size_t live, uintx ratio_as_percentage) {
+  // We want to calculate how much free memory there can be based on the
+  // amount of live data currently in the old gen. Using the formula:
+  // ratio * (free + live) = free
+  // Some equation solving later we get:
+  // free = (live * ratio) / (1 - ratio)
+
+  const double ratio = ratio_as_percentage / 100.0;
+  const double ratio_inverse = 1.0 - ratio;
+  const double tmp = live * ratio;
+  size_t free = (size_t)(tmp / ratio_inverse);
+
+  return free;
+}
+
+size_t PSAdaptiveSizePolicy::calculated_old_free_size_in_bytes() const {
+  size_t free_size = (size_t)(_promo_size + avg_promoted()->padded_average());
+  size_t live = ParallelScavengeHeap::heap()->old_gen()->used_in_bytes();
+
+  if (MinHeapFreeRatio != 0) {
+    size_t min_free = calculate_free_based_on_live(live, MinHeapFreeRatio);
+    free_size = MAX2(free_size, min_free);
+  }
+
+  if (MaxHeapFreeRatio != 100) {
+    size_t max_free = calculate_free_based_on_live(live, MaxHeapFreeRatio);
+    free_size = MIN2(max_free, free_size);
+  }
+
+  return free_size;
+}
+
 void PSAdaptiveSizePolicy::major_collection_begin() {
   // Update the interval time
   _major_timer.stop();
@@ -1292,3 +1325,18 @@
                           st,
                           PSScavenge::tenuring_threshold());
 }
+
+#ifndef PRODUCT
+
+void TestOldFreeSpaceCalculation_test() {
+  assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(100, 20) == 25, "Calculation of free memory failed");
+  assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(100, 50) == 100, "Calculation of free memory failed");
+  assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(100, 60) == 150, "Calculation of free memory failed");
+  assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(100, 75) == 300, "Calculation of free memory failed");
+  assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(400, 20) == 100, "Calculation of free memory failed");
+  assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(400, 50) == 400, "Calculation of free memory failed");
+  assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(400, 60) == 600, "Calculation of free memory failed");
+  assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(400, 75) == 1200, "Calculation of free memory failed");
+}
+
+#endif /* !PRODUCT */
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp	Wed Jan 29 23:17:05 2014 +0100
@@ -240,7 +240,6 @@
   void major_collection_begin();
   void major_collection_end(size_t amount_live, GCCause::Cause gc_cause);
 
-  //
   void tenured_allocation(size_t size) {
     _avg_pretenured->sample(size);
   }
@@ -248,9 +247,9 @@
   // Accessors
   // NEEDS_CLEANUP   should use sizes.hpp
 
-  size_t calculated_old_free_size_in_bytes() const {
-    return (size_t)(_promo_size + avg_promoted()->padded_average());
-  }
+  static size_t calculate_free_based_on_live(size_t live, uintx ratio_as_percentage);
+
+  size_t calculated_old_free_size_in_bytes() const;
 
   size_t average_old_live_in_bytes() const {
     return (size_t) avg_old_live()->average();
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp	Wed Jan 29 23:17:05 2014 +0100
@@ -529,8 +529,19 @@
           counters->update_survivor_overflowed(_survivor_overflow);
         }
 
+        size_t max_young_size = young_gen->max_size();
+
+        // Deciding a free ratio in the young generation is tricky, so if
+        // MinHeapFreeRatio or MaxHeapFreeRatio are in use (implicating
+        // that the old generation size may have been limited because of them) we
+        // should then limit our young generation size using NewRatio to have it
+        // follow the old generation size.
+        if (MinHeapFreeRatio != 0 || MaxHeapFreeRatio != 100) {
+          max_young_size = MIN2(old_gen->capacity_in_bytes() / NewRatio, young_gen->max_size());
+        }
+
         size_t survivor_limit =
-          size_policy->max_survivor_size(young_gen->max_size());
+          size_policy->max_survivor_size(max_young_size);
         _tenuring_threshold =
           size_policy->compute_survivor_space_size_and_threshold(
                                                            _survivor_overflow,
@@ -553,8 +564,7 @@
         // Do call at minor collections?
         // Don't check if the size_policy is ready at this
         // level.  Let the size_policy check that internally.
-        if (UseAdaptiveSizePolicy &&
-            UseAdaptiveGenerationSizePolicyAtMinorCollection &&
+        if (UseAdaptiveGenerationSizePolicyAtMinorCollection &&
             ((gc_cause != GCCause::_java_lang_system_gc) ||
               UseAdaptiveSizePolicyWithSystemGC)) {
 
@@ -568,7 +578,7 @@
           size_t eden_live = young_gen->eden_space()->used_in_bytes();
           size_t cur_eden = young_gen->eden_space()->capacity_in_bytes();
           size_t max_old_gen_size = old_gen->max_gen_size();
-          size_t max_eden_size = young_gen->max_size() -
+          size_t max_eden_size = max_young_size -
             young_gen->from_space()->capacity_in_bytes() -
             young_gen->to_space()->capacity_in_bytes();
 
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp	Wed Jan 29 23:17:05 2014 +0100
@@ -127,7 +127,7 @@
   void adjust_pointers();
   void compact();
 
-  // Called during/after gc
+  // Called during/after GC
   void swap_spaces();
 
   // Resize generation using suggested free space size and survivor size
@@ -146,14 +146,14 @@
   size_t free_in_words() const;
 
   // The max this generation can grow to
-  size_t max_size() const            { return _reserved.byte_size(); }
+  size_t max_size() const { return _reserved.byte_size(); }
 
   // The max this generation can grow to if the boundary between
   // the generations are allowed to move.
   size_t gen_size_limit() const { return _max_gen_size; }
 
   bool is_maximal_no_gc() const {
-    return true;  // never expands except at a GC
+    return true;  // Never expands except at a GC
   }
 
   // Allocation
--- a/hotspot/src/share/vm/prims/jni.cpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/prims/jni.cpp	Wed Jan 29 23:17:05 2014 +0100
@@ -5061,6 +5061,7 @@
 void TestMetaspaceAux_test();
 void TestMetachunk_test();
 void TestVirtualSpaceNode_test();
+void TestOldFreeSpaceCalculation_test();
 #if INCLUDE_ALL_GCS
 void TestG1BiasedArray_test();
 #endif
@@ -5081,6 +5082,7 @@
     run_unit_test(QuickSort::test_quick_sort());
     run_unit_test(AltHashing::test_alt_hash());
     run_unit_test(test_loggc_filename());
+    run_unit_test(TestOldFreeSpaceCalculation_test());
 #if INCLUDE_VM_STRUCTS
     run_unit_test(VMStructs::test());
 #endif
--- a/hotspot/src/share/vm/runtime/arguments.cpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/runtime/arguments.cpp	Wed Jan 29 23:17:05 2014 +0100
@@ -1569,6 +1569,16 @@
     vm_exit(1);
   }
 
+  if (UseAdaptiveSizePolicy) {
+    // We don't want to limit adaptive heap sizing's freedom to adjust the heap
+    // unless the user actually sets these flags.
+    if (FLAG_IS_DEFAULT(MinHeapFreeRatio)) {
+      FLAG_SET_DEFAULT(MinHeapFreeRatio, 0);
+    }
+    if (FLAG_IS_DEFAULT(MaxHeapFreeRatio)) {
+      FLAG_SET_DEFAULT(MaxHeapFreeRatio, 100);
+    }
+  }
 
   // If InitialSurvivorRatio or MinSurvivorRatio were not specified, but the
   // SurvivorRatio has been set, reset their default values to SurvivorRatio +
@@ -1844,7 +1854,7 @@
 }
 
 bool Arguments::verify_percentage(uintx value, const char* name) {
-  if (value <= 100) {
+  if (is_percentage(value)) {
     return true;
   }
   jio_fprintf(defaultStream::error_stream(),
@@ -1932,6 +1942,34 @@
   return count_p < 2 && count_t < 2;
 }
 
+bool Arguments::verify_MinHeapFreeRatio(FormatBuffer<80>& err_msg, uintx min_heap_free_ratio) {
+  if (!is_percentage(min_heap_free_ratio)) {
+    err_msg.print("MinHeapFreeRatio must have a value between 0 and 100");
+    return false;
+  }
+  if (min_heap_free_ratio > MaxHeapFreeRatio) {
+    err_msg.print("MinHeapFreeRatio (" UINTX_FORMAT ") must be less than or "
+                  "equal to MaxHeapFreeRatio (" UINTX_FORMAT ")", min_heap_free_ratio,
+                  MaxHeapFreeRatio);
+    return false;
+  }
+  return true;
+}
+
+bool Arguments::verify_MaxHeapFreeRatio(FormatBuffer<80>& err_msg, uintx max_heap_free_ratio) {
+  if (!is_percentage(max_heap_free_ratio)) {
+    err_msg.print("MaxHeapFreeRatio must have a value between 0 and 100");
+    return false;
+  }
+  if (max_heap_free_ratio < MinHeapFreeRatio) {
+    err_msg.print("MaxHeapFreeRatio (" UINTX_FORMAT ") must be greater than or "
+                  "equal to MinHeapFreeRatio (" UINTX_FORMAT ")", max_heap_free_ratio,
+                  MinHeapFreeRatio);
+    return false;
+  }
+  return true;
+}
+
 // Check consistency of GC selection
 bool Arguments::check_gc_consistency() {
   check_gclog_consistency();
@@ -2037,8 +2075,6 @@
   status = status && verify_interval(AdaptiveSizePolicyWeight, 0, 100,
                               "AdaptiveSizePolicyWeight");
   status = status && verify_percentage(ThresholdTolerance, "ThresholdTolerance");
-  status = status && verify_percentage(MinHeapFreeRatio, "MinHeapFreeRatio");
-  status = status && verify_percentage(MaxHeapFreeRatio, "MaxHeapFreeRatio");
 
   // Divide by bucket size to prevent a large size from causing rollover when
   // calculating amount of memory needed to be allocated for the String table.
@@ -2048,15 +2084,19 @@
   status = status && verify_interval(SymbolTableSize, minimumSymbolTableSize,
     (max_uintx / SymbolTable::bucket_size()), "SymbolTable size");
 
-  if (MinHeapFreeRatio > MaxHeapFreeRatio) {
-    jio_fprintf(defaultStream::error_stream(),
-                "MinHeapFreeRatio (" UINTX_FORMAT ") must be less than or "
-                "equal to MaxHeapFreeRatio (" UINTX_FORMAT ")\n",
-                MinHeapFreeRatio, MaxHeapFreeRatio);
-    status = false;
+  {
+    // Using "else if" below to avoid printing two error messages if min > max.
+    // This will also prevent us from reporting both min>100 and max>100 at the
+    // same time, but that is less annoying than printing two identical errors IMHO.
+    FormatBuffer<80> err_msg("");
+    if (!verify_MinHeapFreeRatio(err_msg, MinHeapFreeRatio)) {
+      jio_fprintf(defaultStream::error_stream(), "%s\n", err_msg.buffer());
+      status = false;
+    } else if (!verify_MaxHeapFreeRatio(err_msg, MaxHeapFreeRatio)) {
+      jio_fprintf(defaultStream::error_stream(), "%s\n", err_msg.buffer());
+      status = false;
+    }
   }
-  // Keeping the heap 100% free is hard ;-) so limit it to 99%.
-  MinHeapFreeRatio = MIN2(MinHeapFreeRatio, (uintx) 99);
 
   // Min/MaxMetaspaceFreeRatio
   status = status && verify_percentage(MinMetaspaceFreeRatio, "MinMetaspaceFreeRatio");
@@ -2689,7 +2729,7 @@
     } else if (match_option(option, "-Xmaxf", &tail)) {
       char* err;
       int maxf = (int)(strtod(tail, &err) * 100);
-      if (*err != '\0' || maxf < 0 || maxf > 100) {
+      if (*err != '\0' || *tail == '\0' || maxf < 0 || maxf > 100) {
         jio_fprintf(defaultStream::error_stream(),
                     "Bad max heap free percentage size: %s\n",
                     option->optionString);
@@ -2701,7 +2741,7 @@
     } else if (match_option(option, "-Xminf", &tail)) {
       char* err;
       int minf = (int)(strtod(tail, &err) * 100);
-      if (*err != '\0' || minf < 0 || minf > 100) {
+      if (*err != '\0' || *tail == '\0' || minf < 0 || minf > 100) {
         jio_fprintf(defaultStream::error_stream(),
                     "Bad min heap free percentage size: %s\n",
                     option->optionString);
@@ -3646,9 +3686,9 @@
   // Set per-collector flags
   if (UseParallelGC || UseParallelOldGC) {
     set_parallel_gc_flags();
-  } else if (UseConcMarkSweepGC) { // should be done before ParNew check below
+  } else if (UseConcMarkSweepGC) { // Should be done before ParNew check below
     set_cms_and_parnew_gc_flags();
-  } else if (UseParNewGC) {  // skipped if CMS is set above
+  } else if (UseParNewGC) {  // Skipped if CMS is set above
     set_parnew_gc_flags();
   } else if (UseG1GC) {
     set_g1_gc_flags();
@@ -3662,6 +3702,10 @@
               " using -XX:ParallelGCThreads=N");
     }
   }
+  if (MinHeapFreeRatio == 100) {
+    // Keeping the heap 100% free is hard ;-) so limit it to 99%.
+    FLAG_SET_ERGO(uintx, MinHeapFreeRatio, 99);
+  }
 #else // INCLUDE_ALL_GCS
   assert(verify_serial_gc_flags(), "SerialGC unset");
 #endif // INCLUDE_ALL_GCS
--- a/hotspot/src/share/vm/runtime/arguments.hpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/runtime/arguments.hpp	Wed Jan 29 23:17:05 2014 +0100
@@ -27,6 +27,7 @@
 
 #include "runtime/java.hpp"
 #include "runtime/perfData.hpp"
+#include "utilities/debug.hpp"
 #include "utilities/top.hpp"
 
 // Arguments parses the command line and recognizes options
@@ -370,11 +371,16 @@
   static jint parse_vm_init_args(const JavaVMInitArgs* args);
   static jint parse_each_vm_init_arg(const JavaVMInitArgs* args, SysClassPath* scp_p, bool* scp_assembly_required_p, Flag::Flags origin);
   static jint finalize_vm_init_args(SysClassPath* scp_p, bool scp_assembly_required);
-  static bool is_bad_option(const JavaVMOption* option, jboolean ignore,
-    const char* option_type);
+  static bool is_bad_option(const JavaVMOption* option, jboolean ignore, const char* option_type);
+
   static bool is_bad_option(const JavaVMOption* option, jboolean ignore) {
     return is_bad_option(option, ignore, NULL);
   }
+
+  static bool is_percentage(uintx val) {
+    return val <= 100;
+  }
+
   static bool verify_interval(uintx val, uintx min,
                               uintx max, const char* name);
   static bool verify_min_value(intx val, intx min, const char* name);
@@ -440,6 +446,15 @@
   static jint apply_ergo();
   // Adjusts the arguments after the OS have adjusted the arguments
   static jint adjust_after_os();
+
+  // Verifies that the given value will fit as a MinHeapFreeRatio. If not, an error
+  // message is returned in the provided buffer.
+  static bool verify_MinHeapFreeRatio(FormatBuffer<80>& err_msg, uintx min_heap_free_ratio);
+
+  // Verifies that the given value will fit as a MaxHeapFreeRatio. If not, an error
+  // message is returned in the provided buffer.
+  static bool verify_MaxHeapFreeRatio(FormatBuffer<80>& err_msg, uintx max_heap_free_ratio);
+
   // Check for consistency in the selection of the garbage collector.
   static bool check_gc_consistency();
   static void check_deprecated_gcs();
--- a/hotspot/src/share/vm/runtime/globals.hpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/runtime/globals.hpp	Wed Jan 29 23:17:05 2014 +0100
@@ -3135,15 +3135,15 @@
           "Maximum size of class area in Metaspace when compressed "        \
           "class pointers are used")                                        \
                                                                             \
-  product(uintx, MinHeapFreeRatio,    40,                                   \
+  manageable(uintx, MinHeapFreeRatio, 40,                                   \
           "The minimum percentage of heap free after GC to avoid expansion."\
-          " For most GCs this applies to the old generation. In G1 it"      \
-          " applies to the whole heap. Not supported by ParallelGC.")       \
-                                                                            \
-  product(uintx, MaxHeapFreeRatio,    70,                                   \
+          " For most GCs this applies to the old generation. In G1 and"     \
+          " ParallelGC it applies to the whole heap.")                      \
+                                                                            \
+  manageable(uintx, MaxHeapFreeRatio, 70,                                   \
           "The maximum percentage of heap free after GC to avoid shrinking."\
-          " For most GCs this applies to the old generation. In G1 it"      \
-          " applies to the whole heap. Not supported by ParallelGC.")       \
+          " For most GCs this applies to the old generation. In G1 and"     \
+          " ParallelGC it applies to the whole heap.")                      \
                                                                             \
   product(intx, SoftRefLRUPolicyMSPerMB, 1000,                              \
           "Number of milliseconds per MB of free space in the heap")        \
--- a/hotspot/src/share/vm/services/attachListener.cpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/services/attachListener.cpp	Wed Jan 29 23:17:05 2014 +0100
@@ -282,6 +282,20 @@
       return JNI_ERR;
     }
   }
+
+  if (strncmp(name, "MaxHeapFreeRatio", 17) == 0) {
+    FormatBuffer<80> err_msg("");
+    if (!Arguments::verify_MaxHeapFreeRatio(err_msg, value)) {
+      out->print_cr(err_msg.buffer());
+      return JNI_ERR;
+    }
+  } else if (strncmp(name, "MinHeapFreeRatio", 17) == 0) {
+    FormatBuffer<80> err_msg("");
+    if (!Arguments::verify_MinHeapFreeRatio(err_msg, value)) {
+      out->print_cr(err_msg.buffer());
+      return JNI_ERR;
+    }
+  }
   bool res = CommandLineFlags::uintxAtPut((char*)name, &value, Flag::ATTACH_ON_DEMAND);
   if (! res) {
     out->print_cr("setting flag %s failed", name);
--- a/hotspot/src/share/vm/services/management.cpp	Wed Jan 15 07:20:04 2014 -0800
+++ b/hotspot/src/share/vm/services/management.cpp	Wed Jan 29 23:17:05 2014 +0100
@@ -1833,6 +1833,18 @@
     succeed = CommandLineFlags::intxAtPut(name, &ivalue, Flag::MANAGEMENT);
   } else if (flag->is_uintx()) {
     uintx uvalue = (uintx)new_value.j;
+
+    if (strncmp(name, "MaxHeapFreeRatio", 17) == 0) {
+      FormatBuffer<80> err_msg("");
+      if (!Arguments::verify_MaxHeapFreeRatio(err_msg, uvalue)) {
+        THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), err_msg.buffer());
+      }
+    } else if (strncmp(name, "MinHeapFreeRatio", 17) == 0) {
+      FormatBuffer<80> err_msg("");
+      if (!Arguments::verify_MinHeapFreeRatio(err_msg, uvalue)) {
+        THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), err_msg.buffer());
+      }
+    }
     succeed = CommandLineFlags::uintxAtPut(name, &uvalue, Flag::MANAGEMENT);
   } else if (flag->is_uint64_t()) {
     uint64_t uvalue = (uint64_t)new_value.j;