diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 9c8acf346ff42..05e8be5e95391 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -2243,6 +2243,7 @@ double gc_heap::short_plugs_pad_ratio = 0; #endif //SHORT_PLUGS int gc_heap::generation_skip_ratio_threshold = 0; +int gc_heap::conserve_mem_setting = 0; uint64_t gc_heap::suspended_start_time = 0; uint64_t gc_heap::end_gc_time = 0; @@ -11858,9 +11859,9 @@ void gc_heap::make_generation (int gen_num, heap_segment* seg, uint8_t* start) #endif //DOUBLY_LINKED_FL #ifdef FREE_USAGE_STATS - memset (gen->gen_free_spaces, 0, sizeof (gen.gen_free_spaces)); - memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen.gen_current_pinned_free_spaces)); - memset (gen->gen_plugs, 0, sizeof (gen.gen_plugs)); + memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces)); + memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces)); + memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs)); #endif //FREE_USAGE_STATS } @@ -12455,6 +12456,14 @@ gc_heap::init_semi_shared() #endif //FEATURE_LOH_COMPACTION #endif //FEATURE_EVENT_TRACE + conserve_mem_setting = (int)GCConfig::GetGCConserveMem(); + if (conserve_mem_setting < 0) + conserve_mem_setting = 0; + if (conserve_mem_setting > 9) + conserve_mem_setting = 9; + + dprintf (1, ("conserve_mem_setting = %d", conserve_mem_setting)); + ret = 1; cleanup: @@ -16961,7 +16970,6 @@ void gc_heap::init_free_and_plug() #else memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces)); #endif //DOUBLY_LINKED_FL - memset (gen->gen_plugs_allocated_in_free, 0, sizeof (gen->gen_plugs_allocated_in_free)); memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs)); memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces)); } @@ -16979,7 +16987,7 @@ void gc_heap::init_free_and_plug() void gc_heap::print_free_and_plug (const char* msg) { -#if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF) +#ifdef FREE_USAGE_STATS int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1)); for (int i = 0; i <= older_gen; i++) { @@ -17000,7 +17008,7 @@ void gc_heap::print_free_and_plug (const char* msg) } #else UNREFERENCED_PARAMETER(msg); -#endif //FREE_USAGE_STATS && SIMPLE_DPRINTF +#endif //FREE_USAGE_STATS } // replace with allocator::first_suitable_bucket @@ -17072,8 +17080,8 @@ void gc_heap::add_gen_free (int gen_number, size_t free_size) (gen->gen_free_spaces[i])++; if (gen_number == max_generation) { - dprintf (3, ("Mb b%d: f+ %Id (%Id->%Id)", - i, free_size, (gen->gen_free_spaces[i]).num_items, (gen->gen_free_spaces[i]).total_size)); + dprintf (3, ("Mb b%d: f+ %Id (%Id)", + i, free_size, gen->gen_free_spaces[i])); } #else UNREFERENCED_PARAMETER(gen_number); @@ -17095,8 +17103,8 @@ void gc_heap::remove_gen_free (int gen_number, size_t free_size) (gen->gen_free_spaces[i])--; if (gen_number == max_generation) { - dprintf (3, ("Mb b%d: f- %Id (%Id->%Id)", - i, free_size, (gen->gen_free_spaces[i]).num_items, (gen->gen_free_spaces[i]).total_size)); + dprintf (3, ("Mb b%d: f- %Id (%Id)", + i, free_size, gen->gen_free_spaces[i])); } #else UNREFERENCED_PARAMETER(gen_number); @@ -18289,6 +18297,37 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation, } } + if ((conserve_mem_setting != 0) && (n == max_generation)) + { + float frag_limit = 1.0f - conserve_mem_setting / 10.0f; + + size_t loh_size = get_total_gen_size (loh_generation); + size_t gen2_size = get_total_gen_size (max_generation); + float loh_frag_ratio = 0.0f; + float combined_frag_ratio = 0.0f; + if (loh_size != 0) + { + size_t loh_frag = get_total_gen_fragmentation (loh_generation); + size_t gen2_frag = get_total_gen_fragmentation (max_generation); + loh_frag_ratio = (float)loh_frag / (float)loh_size; + combined_frag_ratio = (float)(gen2_frag + loh_frag) / (float)(gen2_size + loh_size); + } + if (combined_frag_ratio > frag_limit) + { + dprintf (GTC_LOG, ("combined frag: %f > limit %f, loh frag: %f", combined_frag_ratio, frag_limit, loh_frag_ratio)); + gc_data_global.gen_to_condemn_reasons.set_condition (gen_max_high_frag_p); + + n = max_generation; + *blocking_collection_p = TRUE; + if (loh_frag_ratio > frag_limit) + { + settings.loh_compaction = TRUE; + + dprintf (GTC_LOG, ("compacting LOH due to GCConserveMem setting")); + } + } + } + #ifdef BGC_SERVO_TUNING if (bgc_tuning::should_trigger_ngc2()) { @@ -23360,6 +23399,21 @@ size_t gc_heap::get_total_gen_estimated_reclaim (int gen_number) return total_estimated_reclaim; } +size_t gc_heap::get_total_gen_size (int gen_number) +{ +#ifdef MULTIPLE_HEAPS + size_t size = 0; + for (int hn = 0; hn < gc_heap::n_heaps; hn++) + { + gc_heap* hp = gc_heap::g_heaps[hn]; + size += hp->generation_size (gen_number); + } +#else + size_t size = generation_size (gen_number); +#endif //MULTIPLE_HEAPS + return size; +} + size_t gc_heap::committed_size() { size_t total_committed = 0; @@ -25521,7 +25575,7 @@ BOOL gc_heap::plan_loh() void gc_heap::compact_loh() { - assert (loh_compaction_requested() || heap_hard_limit); + assert (loh_compaction_requested() || heap_hard_limit || conserve_mem_setting); #ifdef FEATURE_EVENT_TRACE uint64_t start_time, end_time; @@ -37294,6 +37348,19 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd, cst = min (1.0f, float (out) / float (dd_begin_data_size (dd))); f = surv_to_growth (cst, limit, max_limit); + if (conserve_mem_setting != 0) + { + // if this is set, compute a growth factor based on it. + // example: a setting of 6 means we have a goal of 60% live data + // this means we allow 40% fragmentation + // to keep heap size stable, we only use half of that (20%) for new allocation + // f is (live data + new allocation)/(live data), so would be (60% + 20%) / 60% or 1.33 + float f_conserve = ((10.0f / conserve_mem_setting) - 1) * 0.5f + 1.0f; + + // use the smaller one + f = min (f, f_conserve); + } + size_t max_growth_size = (size_t)(max_size / f); if (current_size >= max_growth_size) { @@ -37317,6 +37384,7 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd, #ifdef BGC_SERVO_TUNING !bgc_tuning::fl_tuning_triggered && #endif //BGC_SERVO_TUNING + (conserve_mem_setting == 0) && (dd_fragmentation (dd) > ((size_t)((f-1)*current_size)))) { //reducing allocation in case of fragmentation diff --git a/src/coreclr/gc/gcconfig.h b/src/coreclr/gc/gcconfig.h index 8a9112ec48084..886ea17743880 100644 --- a/src/coreclr/gc/gcconfig.h +++ b/src/coreclr/gc/gcconfig.h @@ -133,6 +133,7 @@ class GCConfigStringHolder INT_CONFIG (GCHeapHardLimitLOHPercent, "GCHeapHardLimitLOHPercent", "System.GC.HeapHardLimitLOHPercent", 0, "Specifies the GC heap LOH usage as a percentage of the total memory") \ INT_CONFIG (GCHeapHardLimitPOHPercent, "GCHeapHardLimitPOHPercent", "System.GC.HeapHardLimitPOHPercent", 0, "Specifies the GC heap POH usage as a percentage of the total memory") \ INT_CONFIG (GCEnabledInstructionSets, "GCEnabledInstructionSets", NULL, -1, "Specifies whether GC can use AVX2 or AVX512F - 0 for neither, 1 for AVX2, 3 for AVX512F")\ + INT_CONFIG (GCConserveMem, "GCConserveMemory", NULL, 0, "Specifies how hard GC should try to conserve memory - values 0-9") \ // This class is responsible for retreiving configuration information // for how the GC should operate. diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index bbc7e055f2661..327f7481757fb 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -3142,6 +3142,8 @@ class gc_heap PER_HEAP_ISOLATED size_t get_total_gen_estimated_reclaim (int gen_number); PER_HEAP_ISOLATED + size_t get_total_gen_size (int gen_number); + PER_HEAP_ISOLATED void get_memory_info (uint32_t* memory_load, uint64_t* available_physical=NULL, uint64_t* available_page_file=NULL); @@ -4639,6 +4641,9 @@ class gc_heap PER_HEAP_ISOLATED int generation_skip_ratio_threshold; + PER_HEAP_ISOLATED + int conserve_mem_setting; + PER_HEAP BOOL gen0_bricks_cleared; PER_HEAP