Skip to content

Commit 57b971b

Browse files
authored
Allow user to specify independent heap hard limits for different object heaps. (#37166)
1 parent 6176924 commit 57b971b

File tree

4 files changed

+193
-23
lines changed

4 files changed

+193
-23
lines changed

src/coreclr/src/gc/env/gcenv.base.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ inline HRESULT HRESULT_FROM_WIN32(unsigned long x)
7676
#define S_OK 0x0
7777
#define E_FAIL 0x80004005
7878
#define E_OUTOFMEMORY 0x8007000E
79+
#define E_INVALIDARG 0x80070057
7980
#define COR_E_EXECUTIONENGINE 0x80131506
8081
#define CLR_E_GC_BAD_AFFINITY_CONFIG 0x8013200A
8182
#define CLR_E_GC_BAD_AFFINITY_CONFIG_FORMAT 0x8013200B

src/coreclr/src/gc/gc.cpp

Lines changed: 181 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,6 +2210,8 @@ uint64_t gc_heap::entry_available_physical_mem = 0;
22102210

22112211
size_t gc_heap::heap_hard_limit = 0;
22122212

2213+
size_t gc_heap::heap_hard_limit_oh[total_oh_count - 1] = {0, 0, 0};
2214+
22132215
bool affinity_config_specified_p = false;
22142216
#ifdef BACKGROUND_GC
22152217
GCEvent gc_heap::bgc_start_event;
@@ -3924,7 +3926,8 @@ struct initial_memory_details
39243926
{
39253927
ALLATONCE = 1,
39263928
EACH_GENERATION,
3927-
EACH_BLOCK
3929+
EACH_BLOCK,
3930+
ALLATONCE_SEPARATED_POH
39283931
};
39293932

39303933
size_t allocation_pattern;
@@ -3970,7 +3973,7 @@ struct initial_memory_details
39703973

39713974
initial_memory_details memory_details;
39723975

3973-
BOOL gc_heap::reserve_initial_memory (size_t normal_size, size_t large_size, size_t pinned_size, int num_heaps, bool use_large_pages_p)
3976+
BOOL gc_heap::reserve_initial_memory (size_t normal_size, size_t large_size, size_t pinned_size, int num_heaps, bool use_large_pages_p, bool separated_poh_p)
39743977
{
39753978
BOOL reserve_success = FALSE;
39763979

@@ -4014,23 +4017,52 @@ BOOL gc_heap::reserve_initial_memory (size_t normal_size, size_t large_size, siz
40144017
return FALSE;
40154018
}
40164019

4017-
size_t requestedMemory = memory_details.block_count * (normal_size + large_size + pinned_size);
4020+
size_t temp_pinned_size = (separated_poh_p ? 0 : pinned_size);
4021+
size_t separate_pinned_size = memory_details.block_count * pinned_size;
4022+
size_t requestedMemory = memory_details.block_count * (normal_size + large_size + temp_pinned_size);
40184023

40194024
uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory, use_large_pages_p);
4025+
uint8_t* separated_poh_block = nullptr;
4026+
if (allatonce_block && separated_poh_p)
4027+
{
4028+
separated_poh_block = (uint8_t*)virtual_alloc (separate_pinned_size, false);
4029+
if (!separated_poh_block)
4030+
{
4031+
virtual_free (allatonce_block, requestedMemory);
4032+
allatonce_block = nullptr;
4033+
}
4034+
}
40204035
if (allatonce_block)
40214036
{
4022-
g_gc_lowest_address = allatonce_block;
4023-
g_gc_highest_address = allatonce_block + requestedMemory;
4024-
memory_details.allocation_pattern = initial_memory_details::ALLATONCE;
4037+
if (separated_poh_p)
4038+
{
4039+
g_gc_lowest_address = min (allatonce_block, separated_poh_block);
4040+
g_gc_highest_address = max ((allatonce_block + requestedMemory), (separated_poh_block + separate_pinned_size));
4041+
memory_details.allocation_pattern = initial_memory_details::ALLATONCE_SEPARATED_POH;
4042+
}
4043+
else
4044+
{
4045+
g_gc_lowest_address = allatonce_block;
4046+
g_gc_highest_address = allatonce_block + requestedMemory;
4047+
memory_details.allocation_pattern = initial_memory_details::ALLATONCE;
4048+
}
40254049

40264050
for (int i = 0; i < memory_details.block_count; i++)
40274051
{
40284052
memory_details.initial_normal_heap[i].memory_base = allatonce_block +
40294053
(i * normal_size);
40304054
memory_details.initial_large_heap[i].memory_base = allatonce_block +
40314055
(memory_details.block_count * normal_size) + (i * large_size);
4032-
memory_details.initial_pinned_heap[i].memory_base = allatonce_block +
4033-
(memory_details.block_count * (normal_size + large_size)) + (i * pinned_size);
4056+
if (separated_poh_p)
4057+
{
4058+
memory_details.initial_pinned_heap[i].memory_base = separated_poh_block +
4059+
(i * pinned_size);
4060+
}
4061+
else
4062+
{
4063+
memory_details.initial_pinned_heap[i].memory_base = allatonce_block +
4064+
(memory_details.block_count * (normal_size + large_size)) + (i * pinned_size);
4065+
}
40344066

40354067
reserve_success = TRUE;
40364068
}
@@ -4040,7 +4072,7 @@ BOOL gc_heap::reserve_initial_memory (size_t normal_size, size_t large_size, siz
40404072
// try to allocate 3 blocks
40414073
uint8_t* b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size, use_large_pages_p);
40424074
uint8_t* b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size, use_large_pages_p);
4043-
uint8_t* b3 = (uint8_t*)virtual_alloc (memory_details.block_count * pinned_size, use_large_pages_p);
4075+
uint8_t* b3 = (uint8_t*)virtual_alloc (memory_details.block_count * pinned_size, use_large_pages_p && !separated_poh_p);
40444076

40454077
if (b1 && b2 && b3)
40464078
{
@@ -4114,10 +4146,18 @@ void gc_heap::destroy_initial_memory()
41144146
if (memory_details.initial_memory != NULL)
41154147
{
41164148
if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE)
4149+
{
4150+
virtual_free(memory_details.initial_memory[0].memory_base,
4151+
memory_details.block_count*(memory_details.block_size_normal +
4152+
memory_details.block_size_large + memory_details.block_size_pinned));
4153+
}
4154+
else if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE_SEPARATED_POH)
41174155
{
41184156
virtual_free(memory_details.initial_memory[0].memory_base,
41194157
memory_details.block_count*(memory_details.block_size_normal +
41204158
memory_details.block_size_large));
4159+
virtual_free(memory_details.initial_pinned_heap[0].memory_base,
4160+
memory_details.block_count*(memory_details.block_size_pinned));
41214161
}
41224162
else if (memory_details.allocation_pattern == initial_memory_details::EACH_GENERATION)
41234163
{
@@ -5566,10 +5606,16 @@ bool gc_heap::virtual_commit (void* address, size_t size, gc_oh_num oh, int h_nu
55665606
if (heap_hard_limit)
55675607
{
55685608
check_commit_cs.Enter();
5569-
committed_by_oh[oh] += size;
55705609
bool exceeded_p = false;
55715610

5572-
if ((current_total_committed + size) > heap_hard_limit)
5611+
if (heap_hard_limit_oh[0] != 0)
5612+
{
5613+
if ((committed_by_oh[oh] + size) > heap_hard_limit_oh[oh])
5614+
{
5615+
exceeded_p = true;
5616+
}
5617+
}
5618+
else if ((current_total_committed + size) > heap_hard_limit)
55735619
{
55745620
dprintf (1, ("%Id + %Id = %Id > limit %Id ",
55755621
current_total_committed, size,
@@ -5578,8 +5624,10 @@ bool gc_heap::virtual_commit (void* address, size_t size, gc_oh_num oh, int h_nu
55785624

55795625
exceeded_p = true;
55805626
}
5581-
else
5627+
5628+
if (!exceeded_p)
55825629
{
5630+
committed_by_oh[oh] += size;
55835631
current_total_committed += size;
55845632
if (h_number < 0)
55855633
current_total_committed_bookkeeping += size;
@@ -9958,7 +10006,8 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
995810006
check_commit_cs.Initialize();
995910007
}
996010008

9961-
if (!reserve_initial_memory (soh_segment_size, loh_segment_size, poh_segment_size, number_of_heaps, use_large_pages_p))
10009+
bool separated_poh_p = use_large_pages_p && heap_hard_limit_oh[0] && (GCConfig::GetGCHeapHardLimitPOH() == 0) && (GCConfig::GetGCHeapHardLimitPOHPercent() == 0);
10010+
if (!reserve_initial_memory (soh_segment_size, loh_segment_size, poh_segment_size, number_of_heaps, use_large_pages_p, separated_poh_p))
996210011
return E_OUTOFMEMORY;
996310012

996410013
#ifdef CARD_BUNDLE
@@ -12592,7 +12641,7 @@ allocation_state gc_heap::allocate_soh (int gen_number,
1259212641
}
1259312642
else
1259412643
{
12595-
assert (commit_failed_p);
12644+
assert (commit_failed_p || heap_hard_limit);
1259612645
soh_alloc_state = a_state_cant_allocate;
1259712646
oom_r = oom_cant_commit;
1259812647
}
@@ -35103,6 +35152,64 @@ HRESULT GCHeap::Initialize()
3510335152

3510435153
#ifdef HOST_64BIT
3510535154
gc_heap::heap_hard_limit = (size_t)GCConfig::GetGCHeapHardLimit();
35155+
gc_heap::heap_hard_limit_oh[0] = (size_t)GCConfig::GetGCHeapHardLimitSOH();
35156+
gc_heap::heap_hard_limit_oh[1] = (size_t)GCConfig::GetGCHeapHardLimitLOH();
35157+
gc_heap::heap_hard_limit_oh[2] = (size_t)GCConfig::GetGCHeapHardLimitPOH();
35158+
35159+
if (gc_heap::heap_hard_limit_oh[0] || gc_heap::heap_hard_limit_oh[1] || gc_heap::heap_hard_limit_oh[2])
35160+
{
35161+
if (!gc_heap::heap_hard_limit_oh[0])
35162+
{
35163+
return E_INVALIDARG;
35164+
}
35165+
if (!gc_heap::heap_hard_limit_oh[1])
35166+
{
35167+
return E_INVALIDARG;
35168+
}
35169+
if (gc_heap::heap_hard_limit_oh[2] < min_segment_size_hard_limit)
35170+
{
35171+
gc_heap::heap_hard_limit_oh[2] = min_segment_size_hard_limit;
35172+
}
35173+
// This tells the system there is a hard limit, but otherwise we will not compare against this value.
35174+
gc_heap::heap_hard_limit = 1;
35175+
}
35176+
else
35177+
{
35178+
uint32_t percent_of_mem_soh = (uint32_t)GCConfig::GetGCHeapHardLimitSOHPercent();
35179+
uint32_t percent_of_mem_loh = (uint32_t)GCConfig::GetGCHeapHardLimitLOHPercent();
35180+
uint32_t percent_of_mem_poh = (uint32_t)GCConfig::GetGCHeapHardLimitPOHPercent();
35181+
if (percent_of_mem_soh || percent_of_mem_loh || percent_of_mem_poh)
35182+
{
35183+
if ((percent_of_mem_soh <= 0) || (percent_of_mem_soh >= 100))
35184+
{
35185+
return E_INVALIDARG;
35186+
}
35187+
if ((percent_of_mem_loh <= 0) || (percent_of_mem_loh >= 100))
35188+
{
35189+
return E_INVALIDARG;
35190+
}
35191+
else if ((percent_of_mem_poh < 0) || (percent_of_mem_poh >= 100))
35192+
{
35193+
return E_INVALIDARG;
35194+
}
35195+
if ((percent_of_mem_soh + percent_of_mem_loh + percent_of_mem_poh) >= 100)
35196+
{
35197+
return E_INVALIDARG;
35198+
}
35199+
gc_heap::heap_hard_limit_oh[0] = (size_t)(gc_heap::total_physical_mem * (uint64_t)percent_of_mem_soh / (uint64_t)100);
35200+
gc_heap::heap_hard_limit_oh[1] = (size_t)(gc_heap::total_physical_mem * (uint64_t)percent_of_mem_loh / (uint64_t)100);
35201+
if (percent_of_mem_poh == 0)
35202+
{
35203+
gc_heap::heap_hard_limit_oh[2] = min_segment_size_hard_limit;
35204+
}
35205+
else
35206+
{
35207+
gc_heap::heap_hard_limit_oh[2] = (size_t)(gc_heap::total_physical_mem * (uint64_t)percent_of_mem_poh / (uint64_t)100);
35208+
}
35209+
// This tells the system there is a hard limit, but otherwise we will not compare against this value.
35210+
gc_heap::heap_hard_limit = 1;
35211+
}
35212+
}
3510635213

3510735214
if (!(gc_heap::heap_hard_limit))
3510835215
{
@@ -35189,14 +35296,63 @@ HRESULT GCHeap::Initialize()
3518935296

3519035297
size_t seg_size = 0;
3519135298
size_t large_seg_size = 0;
35299+
size_t pin_seg_size = 0;
3519235300

3519335301
if (gc_heap::heap_hard_limit)
3519435302
{
3519535303
gc_heap::use_large_pages_p = GCConfig::GetGCLargePages();
35196-
seg_size = gc_heap::get_segment_size_hard_limit (&nhp, (nhp_from_config == 0));
35197-
gc_heap::soh_segment_size = seg_size;
35198-
large_seg_size = gc_heap::use_large_pages_p ? seg_size : seg_size * 2;
35304+
if (gc_heap::heap_hard_limit_oh[0])
35305+
{
35306+
#ifdef MULTIPLE_HEAPS
35307+
if (nhp_from_config == 0)
35308+
{
35309+
for (int i = 0; i < (total_oh_count - 1); i++)
35310+
{
35311+
uint32_t nhp_oh = (uint32_t)(gc_heap::heap_hard_limit_oh[i] / min_segment_size_hard_limit);
35312+
nhp = min (nhp, nhp_oh);
35313+
}
35314+
if (nhp == 0)
35315+
{
35316+
nhp = 1;
35317+
}
35318+
}
35319+
#endif
35320+
seg_size = gc_heap::heap_hard_limit_oh[0] / nhp;
35321+
large_seg_size = gc_heap::heap_hard_limit_oh[1] / nhp;
35322+
pin_seg_size = gc_heap::heap_hard_limit_oh[2] / nhp;
35323+
35324+
size_t aligned_seg_size = align_on_segment_hard_limit (seg_size);
35325+
size_t aligned_large_seg_size = align_on_segment_hard_limit (large_seg_size);
35326+
size_t aligned_pin_seg_size = align_on_segment_hard_limit (pin_seg_size);
35327+
35328+
if (!gc_heap::use_large_pages_p)
35329+
{
35330+
aligned_seg_size = round_up_power2 (aligned_seg_size);
35331+
aligned_large_seg_size = round_up_power2 (aligned_large_seg_size);
35332+
aligned_pin_seg_size = round_up_power2 (aligned_pin_seg_size);
35333+
}
35334+
35335+
size_t seg_size_from_config = (size_t)GCConfig::GetSegmentSize();
35336+
if (seg_size_from_config)
35337+
{
35338+
size_t aligned_seg_size_config = (gc_heap::use_large_pages_p ? align_on_segment_hard_limit (seg_size) : round_up_power2 (seg_size_from_config));
35339+
aligned_seg_size = max (aligned_seg_size, aligned_seg_size_config);
35340+
aligned_large_seg_size = max (aligned_large_seg_size, aligned_seg_size_config);
35341+
aligned_pin_seg_size = max (aligned_pin_seg_size, aligned_seg_size_config);
35342+
}
3519935343

35344+
seg_size = aligned_seg_size;
35345+
gc_heap::soh_segment_size = seg_size;
35346+
large_seg_size = aligned_large_seg_size;
35347+
pin_seg_size = aligned_pin_seg_size;
35348+
}
35349+
else
35350+
{
35351+
seg_size = gc_heap::get_segment_size_hard_limit (&nhp, (nhp_from_config == 0));
35352+
gc_heap::soh_segment_size = seg_size;
35353+
large_seg_size = gc_heap::use_large_pages_p ? seg_size : seg_size * 2;
35354+
pin_seg_size = large_seg_size;
35355+
}
3520035356
if (gc_heap::use_large_pages_p)
3520135357
gc_heap::min_segment_size = min_segment_size_hard_limit;
3520235358
}
@@ -35205,26 +35361,30 @@ HRESULT GCHeap::Initialize()
3520535361
seg_size = get_valid_segment_size();
3520635362
gc_heap::soh_segment_size = seg_size;
3520735363
large_seg_size = get_valid_segment_size (TRUE);
35364+
pin_seg_size = large_seg_size;
3520835365
}
35366+
assert (g_theGCHeap->IsValidSegmentSize (seg_size));
35367+
assert (g_theGCHeap->IsValidSegmentSize (large_seg_size));
35368+
assert (g_theGCHeap->IsValidSegmentSize (pin_seg_size));
3520935369

3521035370
dprintf (1, ("%d heaps, soh seg size: %Id mb, loh: %Id mb\n",
3521135371
nhp,
3521235372
(seg_size / (size_t)1024 / 1024),
3521335373
(large_seg_size / 1024 / 1024)));
3521435374

35215-
gc_heap::min_uoh_segment_size = large_seg_size;
35375+
gc_heap::min_uoh_segment_size = min (large_seg_size, pin_seg_size);
3521635376

3521735377
if (gc_heap::min_segment_size == 0)
3521835378
{
35219-
gc_heap::min_segment_size = min (seg_size, large_seg_size);
35379+
gc_heap::min_segment_size = min (seg_size, gc_heap::min_uoh_segment_size);
3522035380
}
3522135381
gc_heap::min_segment_size_shr = index_of_highest_set_bit (gc_heap::min_segment_size);
3522235382

3522335383
#ifdef MULTIPLE_HEAPS
3522435384
gc_heap::n_heaps = nhp;
35225-
hr = gc_heap::initialize_gc (seg_size, large_seg_size /*loh_segment_size*/, large_seg_size /*poh_segment_size*/, nhp);
35385+
hr = gc_heap::initialize_gc (seg_size, large_seg_size /*loh_segment_size*/, pin_seg_size /*poh_segment_size*/, nhp);
3522635386
#else
35227-
hr = gc_heap::initialize_gc (seg_size, large_seg_size /*loh_segment_size*/, large_seg_size /*poh_segment_size*/);
35387+
hr = gc_heap::initialize_gc (seg_size, large_seg_size /*loh_segment_size*/, pin_seg_size /*poh_segment_size*/);
3522835388
#endif //MULTIPLE_HEAPS
3522935389

3523035390
if (hr != S_OK)

src/coreclr/src/gc/gcconfig.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,13 @@ class GCConfigStringHolder
122122
INT_CONFIG (BGCFLEnableSmooth, "BGCFLEnableSmooth", NULL, 0, "Enables smoothing") \
123123
INT_CONFIG (BGCFLEnableTBH, "BGCFLEnableTBH", NULL, 0, "Enables TBH") \
124124
INT_CONFIG (BGCFLEnableFF, "BGCFLEnableFF", NULL, 0, "Enables FF") \
125-
INT_CONFIG (BGCG2RatioStep, "BGCG2RatioStep", NULL, 5, "Ratio correction factor for ML loop")
125+
INT_CONFIG (BGCG2RatioStep, "BGCG2RatioStep", NULL, 5, "Ratio correction factor for ML loop") \
126+
INT_CONFIG (GCHeapHardLimitSOH, "GCHeapHardLimitSOH", NULL, 0, "Specifies a hard limit for the GC heap SOH") \
127+
INT_CONFIG (GCHeapHardLimitLOH, "GCHeapHardLimitLOH", NULL, 0, "Specifies a hard limit for the GC heap LOH") \
128+
INT_CONFIG (GCHeapHardLimitPOH, "GCHeapHardLimitPOH", NULL, 0, "Specifies a hard limit for the GC heap POH") \
129+
INT_CONFIG (GCHeapHardLimitSOHPercent, "GCHeapHardLimitSOHPercent", NULL, 0, "Specifies the GC heap SOH usage as a percentage of the total memory") \
130+
INT_CONFIG (GCHeapHardLimitLOHPercent, "GCHeapHardLimitLOHPercent", NULL, 0, "Specifies the GC heap LOH usage as a percentage of the total memory") \
131+
INT_CONFIG (GCHeapHardLimitPOHPercent, "GCHeapHardLimitPOHPercent", NULL, 0, "Specifies the GC heap POH usage as a percentage of the total memory") \
126132

127133
// This class is responsible for retreiving configuration information
128134
// for how the GC should operate.

src/coreclr/src/gc/gcpriv.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,7 @@ class gc_heap
12831283

12841284
protected:
12851285
PER_HEAP_ISOLATED
1286-
BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t pinned_size, int num_heaps, bool use_large_pages_p);
1286+
BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t pinned_size, int num_heaps, bool use_large_pages_p, bool separated_poh_p);
12871287

12881288
PER_HEAP_ISOLATED
12891289
void destroy_initial_memory();
@@ -3383,6 +3383,9 @@ class gc_heap
33833383
PER_HEAP_ISOLATED
33843384
size_t heap_hard_limit;
33853385

3386+
PER_HEAP_ISOLATED
3387+
size_t heap_hard_limit_oh[total_oh_count - 1];
3388+
33863389
PER_HEAP_ISOLATED
33873390
CLRCriticalSection check_commit_cs;
33883391

0 commit comments

Comments
 (0)