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);