Print this page
8236073: G1: Use SoftMaxHeapSize to guide GC heuristics

*** 36,45 **** --- 36,47 ---- #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineThread.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" + #include "gc/g1/g1ConcurrentHeapResize.hpp" + #include "gc/g1/g1ConcurrentHeapResizeThread.hpp" #include "gc/g1/g1DirtyCardQueue.hpp" #include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1FullCollector.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapSizingPolicy.hpp"
*** 1021,1030 **** --- 1023,1035 ---- _ref_processor_cm->verify_no_references_recorded(); // Abandon current iterations of concurrent marking and concurrent // refinement, if any are in progress. concurrent_mark()->concurrent_cycle_abort(); + + // Wait for the finish of concurrent heap resize + concurrent_heap_resize()->wait_for_concurrent_work_finish(); } void G1CollectedHeap::prepare_heap_for_full_collection() { // Make sure we'll choose a new allocation region afterwards. _allocator->release_mutator_alloc_regions();
*** 1047,1068 **** _verifier->verify_region_sets_optional(); _verifier->verify_before_gc(G1HeapVerifier::G1VerifyFull); _verifier->check_bitmaps("Full GC Start"); } ! void G1CollectedHeap::prepare_heap_for_mutators() { hrm()->prepare_for_full_collection_end(); // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(); MetaspaceUtils::verify_metrics(); // Prepare heap for normal collections. assert(num_free_regions() == 0, "we should not have added any free regions"); rebuild_region_sets(false /* free_list_only */); abort_refinement(); ! resize_heap_if_necessary(); // Rebuild the strong code root lists for each region rebuild_strong_code_roots(); // Purge code root memory --- 1052,1075 ---- _verifier->verify_region_sets_optional(); _verifier->verify_before_gc(G1HeapVerifier::G1VerifyFull); _verifier->check_bitmaps("Full GC Start"); } ! void G1CollectedHeap::prepare_heap_for_mutators(bool explicit_gc) { hrm()->prepare_for_full_collection_end(); // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(); MetaspaceUtils::verify_metrics(); // Prepare heap for normal collections. assert(num_free_regions() == 0, "we should not have added any free regions"); rebuild_region_sets(false /* free_list_only */); abort_refinement(); ! ! // If explicit full GC, we will limit within SoftMaxHeapSize ! resize_heap_if_necessary(explicit_gc); // Rebuild the strong code root lists for each region rebuild_strong_code_roots(); // Purge code root memory
*** 1165,1175 **** // out by the GC locker). So, right now, we'll ignore the return value. bool dummy = do_full_collection(true, /* explicit_gc */ clear_all_soft_refs); } ! void G1CollectedHeap::resize_heap_if_necessary() { assert_at_safepoint_on_vm_thread(); // Capacity, free and used after the GC counted as full regions to // include the waste in the following calculations. const size_t capacity_after_gc = capacity(); --- 1172,1182 ---- // out by the GC locker). So, right now, we'll ignore the return value. bool dummy = do_full_collection(true, /* explicit_gc */ clear_all_soft_refs); } ! void G1CollectedHeap::resize_heap_if_necessary(bool limit_within_soft_max_heap_size) { assert_at_safepoint_on_vm_thread(); // Capacity, free and used after the GC counted as full regions to // include the waste in the following calculations. const size_t capacity_after_gc = capacity();
*** 1183,1201 **** const double minimum_free_percentage = (double) MinHeapFreeRatio / 100.0; const double maximum_used_percentage = 1.0 - minimum_free_percentage; const double maximum_free_percentage = (double) MaxHeapFreeRatio / 100.0; const double minimum_used_percentage = 1.0 - maximum_free_percentage; // We have to be careful here as these two calculations can overflow // 32-bit size_t's. double used_after_gc_d = (double) used_after_gc; double minimum_desired_capacity_d = used_after_gc_d / maximum_used_percentage; double maximum_desired_capacity_d = used_after_gc_d / minimum_used_percentage; // Let's make sure that they are both under the max heap size, which // by default will make them fit into a size_t. ! double desired_capacity_upper_bound = (double) MaxHeapSize; minimum_desired_capacity_d = MIN2(minimum_desired_capacity_d, desired_capacity_upper_bound); maximum_desired_capacity_d = MIN2(maximum_desired_capacity_d, desired_capacity_upper_bound); --- 1190,1220 ---- const double minimum_free_percentage = (double) MinHeapFreeRatio / 100.0; const double maximum_used_percentage = 1.0 - minimum_free_percentage; const double maximum_free_percentage = (double) MaxHeapFreeRatio / 100.0; const double minimum_used_percentage = 1.0 - maximum_free_percentage; + // Try to limit within SoftMaxHeapSize if possible + size_t max_heap_size_limit = 0; + size_t min_heap_size_limit = 0; + if (limit_within_soft_max_heap_size && (capacity() <= SoftMaxHeapSize)) { + max_heap_size_limit = SoftMaxHeapSize; + min_heap_size_limit = MIN2(SoftMaxHeapSize, MinHeapSize); + } else { + max_heap_size_limit = MaxHeapSize; + min_heap_size_limit = MinHeapSize; + } + + // We have to be careful here as these two calculations can overflow // 32-bit size_t's. double used_after_gc_d = (double) used_after_gc; double minimum_desired_capacity_d = used_after_gc_d / maximum_used_percentage; double maximum_desired_capacity_d = used_after_gc_d / minimum_used_percentage; // Let's make sure that they are both under the max heap size, which // by default will make them fit into a size_t. ! double desired_capacity_upper_bound = (double) max_heap_size_limit; minimum_desired_capacity_d = MIN2(minimum_desired_capacity_d, desired_capacity_upper_bound); maximum_desired_capacity_d = MIN2(maximum_desired_capacity_d, desired_capacity_upper_bound);
*** 1211,1225 **** minimum_desired_capacity, maximum_desired_capacity); // Should not be greater than the heap max size. No need to adjust // it with respect to the heap min size as it's a lower bound (i.e., // we'll try to make the capacity larger than it, not smaller). ! minimum_desired_capacity = MIN2(minimum_desired_capacity, MaxHeapSize); // Should not be less than the heap min size. No need to adjust it // with respect to the heap max size as it's an upper bound (i.e., // we'll try to make the capacity smaller than it, not greater). ! maximum_desired_capacity = MAX2(maximum_desired_capacity, MinHeapSize); if (capacity_after_gc < minimum_desired_capacity) { // Don't expand unless it's significant size_t expand_bytes = minimum_desired_capacity - capacity_after_gc; --- 1230,1244 ---- minimum_desired_capacity, maximum_desired_capacity); // Should not be greater than the heap max size. No need to adjust // it with respect to the heap min size as it's a lower bound (i.e., // we'll try to make the capacity larger than it, not smaller). ! minimum_desired_capacity = MIN2(minimum_desired_capacity, max_heap_size_limit); // Should not be less than the heap min size. No need to adjust it // with respect to the heap max size as it's an upper bound (i.e., // we'll try to make the capacity smaller than it, not greater). ! maximum_desired_capacity = MAX2(maximum_desired_capacity, min_heap_size_limit); if (capacity_after_gc < minimum_desired_capacity) { // Don't expand unless it's significant size_t expand_bytes = minimum_desired_capacity - capacity_after_gc;
*** 1520,1529 **** --- 1539,1549 ---- _bytes_used_during_gc(0), _archive_allocator(NULL), _survivor_evac_stats("Young", YoungPLABSize, PLABWeight), _old_evac_stats("Old", OldPLABSize, PLABWeight), _expand_heap_after_alloc_failure(true), + _prev_soft_max_heap_size(MaxHeapSize), _g1mm(NULL), _humongous_reclaim_candidates(), _has_humongous_reclaim_candidates(false), _hr_printer(), _collector_state(),
*** 1539,1548 **** --- 1559,1570 ---- _hot_card_cache(NULL), _rem_set(NULL), _cm(NULL), _cm_thread(NULL), _cr(NULL), + _concurrent_heap_resize(NULL), + _concurrent_heap_resize_thread(NULL), _task_queues(NULL), _evacuation_failed(false), _evacuation_failed_info_array(NULL), _preserved_marks_set(true /* in_c_heap */), #ifndef PRODUCT
*** 1805,1814 **** --- 1827,1843 ---- vm_shutdown_during_initialization("Could not create/initialize G1ConcurrentMark"); return JNI_ENOMEM; } _cm_thread = _cm->cm_thread(); + _concurrent_heap_resize = new G1ConcurrentHeapResize(this, _hrm); + if (_concurrent_heap_resize == NULL || !_concurrent_heap_resize->completed_initialization()) { + vm_shutdown_during_initialization("Could not create/initialize G1ConcurrentHeapResize"); + return JNI_ENOMEM; + } + _concurrent_heap_resize_thread = _concurrent_heap_resize->resize_thread(); + // Now expand into the initial heap size. if (!expand(init_byte_size, _workers)) { vm_shutdown_during_initialization("Failed to allocate initial heap."); return JNI_ENOMEM; }
*** 1865,1874 **** --- 1894,1904 ---- // do not continue to execute and access resources (e.g. logging) // that are destroyed during shutdown. _cr->stop(); _young_gen_sampling_thread->stop(); _cm_thread->stop(); + _concurrent_heap_resize_thread->stop(); if (G1StringDedup::is_enabled()) { G1StringDedup::stop(); } }
*** 2943,2952 **** --- 2973,3015 ---- _verifier->verify_after_gc(type); _verifier->check_bitmaps("GC End"); verify_numa_regions("GC End"); } + bool G1CollectedHeap::check_soft_max_heap_size_changed() { + if (AllocateOldGenAt != NULL) { + // Do not support yet + FLAG_SET_ERGO(SoftMaxHeapSize, MaxHeapSize); + return false; + } + if (SoftMaxHeapSize != _prev_soft_max_heap_size) { + if (!is_aligned(SoftMaxHeapSize, HeapAlignment)) { + FLAG_SET_ERGO(SoftMaxHeapSize, align_up(SoftMaxHeapSize, HeapAlignment)); + } + if (SoftMaxHeapSize != _prev_soft_max_heap_size) { + _prev_soft_max_heap_size = SoftMaxHeapSize; + return true; + } + } + return false; + } + + void G1CollectedHeap::adjust_heap_after_young_collection() { + if (concurrent_heap_resize()->resize_thread()->during_cycle()) { + // Do not adjust if concurrent resizing is in progress + return; + } + + double start_time_ms = os::elapsedTime(); + shrink_heap_after_young_collection(); + phase_times()->record_or_add_adjust_heap_time((os::elapsedTime() - start_time_ms) * MILLIUNITS); + + if (!concurrent_heap_resize()->resize_thread()->during_cycle()) { + expand_heap_after_young_collection(); + } + } + void G1CollectedHeap::expand_heap_after_young_collection(){ size_t expand_bytes = _heap_sizing_policy->expansion_amount(); if (expand_bytes > 0) { // No need for an ergo logging here, // expansion_amount() does this when it returns a value > 0.
*** 2956,2965 **** --- 3019,3055 ---- } phase_times()->record_expand_heap_time(expand_ms); } } + void G1CollectedHeap::shrink_heap_after_young_collection() { + // If SoftMaxHeapSize changed, we can trigger an concurrent mark cycle + bool can_trigger_initial_mark = check_soft_max_heap_size_changed(); + + if (capacity() <= SoftMaxHeapSize) { + return; + } + + if (!collector_state()->in_young_only_phase() || + collector_state()->in_initial_mark_gc() || + collector_state()->in_young_gc_before_mixed() || + concurrent_mark()->cm_thread()->during_cycle()) { + // Only do the shrink in normal young GC + // If in concurrent cycle, we will wait for the finish of mixed GCs + return; + } + + if (_heap_sizing_policy->can_shrink_heap_size_to(SoftMaxHeapSize)) { + size_t shrink_bytes = capacity() - SoftMaxHeapSize; + concurrent_heap_resize()->shrink(shrink_bytes); + policy()->record_new_heap_size(SoftMaxHeapSize); + } else if (can_trigger_initial_mark) { + // Trigger a concurrent mark cycle + collector_state()->set_initiate_conc_mark_if_possible(true); + } + } + const char* G1CollectedHeap::young_gc_name() const { if (collector_state()->in_initial_mark_gc()) { return "Pause Young (Concurrent Start)"; } else if (collector_state()->in_young_only_phase()) { if (collector_state()->in_young_gc_before_mixed()) {
*** 3067,3076 **** --- 3157,3170 ---- // NoRefDiscovery object will do this. NoRefDiscovery no_cm_discovery(_ref_processor_cm); policy()->record_collection_pause_start(sample_start_time_sec); + double check_concurrent_resize_start_time_ms = os::elapsedTime(); + concurrent_heap_resize()->check_concurrent_work_finish(); + phase_times()->record_or_add_adjust_heap_time((os::elapsedTime() - check_concurrent_resize_start_time_ms) * MILLIUNITS); + // Forget the current allocation region (we might even choose it to be part // of the collection set!). _allocator->release_mutator_alloc_regions(); calculate_collection_set(evacuation_info, target_pause_time_ms);
*** 3108,3118 **** allocate_dummy_regions(); _allocator->init_mutator_alloc_regions(); ! expand_heap_after_young_collection(); double sample_end_time_sec = os::elapsedTime(); double pause_time_ms = (sample_end_time_sec - sample_start_time_sec) * MILLIUNITS; policy()->record_collection_pause_end(pause_time_ms); } --- 3202,3212 ---- allocate_dummy_regions(); _allocator->init_mutator_alloc_regions(); ! adjust_heap_after_young_collection(); double sample_end_time_sec = os::elapsedTime(); double pause_time_ms = (sample_end_time_sec - sample_start_time_sec) * MILLIUNITS; policy()->record_collection_pause_end(pause_time_ms); }
*** 4647,4656 **** --- 4741,4751 ---- } }; void G1CollectedHeap::tear_down_region_sets(bool free_list_only) { assert_at_safepoint_on_vm_thread(); + assert(_hrm->num_of_concurrent_resizing_regions() == 0, "should not have concurrent resizing regions"); if (!free_list_only) { TearDownRegionSetsClosure cl(&_old_set); heap_region_iterate(&cl);