--- old/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp 2014-02-03 23:18:29.000000000 +0100 +++ new/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp 2014-02-03 23:18:29.000000000 +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(); --- old/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp 2014-02-03 23:18:30.000000000 +0100 +++ new/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp 2014-02-03 23:18:29.000000000 +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 */ --- old/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp 2014-02-03 23:18:30.000000000 +0100 +++ new/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp 2014-02-03 23:18:30.000000000 +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(); --- old/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp 2014-02-03 23:18:31.000000000 +0100 +++ new/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp 2014-02-03 23:18:31.000000000 +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(); --- old/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp 2014-02-03 23:18:32.000000000 +0100 +++ new/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp 2014-02-03 23:18:32.000000000 +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 --- old/src/share/vm/prims/jni.cpp 2014-02-03 23:18:33.000000000 +0100 +++ new/src/share/vm/prims/jni.cpp 2014-02-03 23:18:33.000000000 +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 --- old/src/share/vm/runtime/arguments.cpp 2014-02-03 23:18:34.000000000 +0100 +++ new/src/share/vm/runtime/arguments.cpp 2014-02-03 23:18:34.000000000 +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 --- old/src/share/vm/runtime/arguments.hpp 2014-02-03 23:18:35.000000000 +0100 +++ new/src/share/vm/runtime/arguments.hpp 2014-02-03 23:18:35.000000000 +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(); --- old/src/share/vm/runtime/globals.hpp 2014-02-03 23:18:35.000000000 +0100 +++ new/src/share/vm/runtime/globals.hpp 2014-02-03 23:18:35.000000000 +0100 @@ -3133,15 +3133,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.") \ + " For most GCs this applies to the old generation. In G1 and" \ + " ParallelGC it applies to the whole heap.") \ \ - product(uintx, MaxHeapFreeRatio, 70, \ + 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") \ --- old/src/share/vm/services/attachListener.cpp 2014-02-03 23:18:36.000000000 +0100 +++ new/src/share/vm/services/attachListener.cpp 2014-02-03 23:18:36.000000000 +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); --- old/src/share/vm/services/management.cpp 2014-02-03 23:18:37.000000000 +0100 +++ new/src/share/vm/services/management.cpp 2014-02-03 23:18:37.000000000 +0100 @@ -1830,6 +1830,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;