Skip to content

Commit 21c343b

Browse files
committed
Add GC heap hard limit for 32 bit
1 parent e3684bc commit 21c343b

File tree

2 files changed

+128
-17
lines changed

2 files changed

+128
-17
lines changed

src/coreclr/gc/gc.cpp

Lines changed: 126 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,7 +1925,16 @@ uint8_t* gc_heap::pad_for_alignment_large (uint8_t* newAlloc, int requiredAlignm
19251925
#endif //BACKGROUND_GC && !USE_REGIONS
19261926

19271927
// This is always power of 2.
1928+
#ifdef HOST_64BIT
19281929
const size_t min_segment_size_hard_limit = 1024*1024*16;
1930+
#else //HOST_64BIT
1931+
const size_t min_segment_size_hard_limit = 1024*1024*4;
1932+
#endif //HOST_64BIT
1933+
1934+
#ifndef HOST_64BIT
1935+
// Max size of heap hard limit (2^31) to be able to be aligned and rounded up on power of 2 and not overflow
1936+
const size_t max_heap_hard_limit = (size_t)2 * (size_t)1024 * (size_t)1024 * (size_t)1024;
1937+
#endif //!HOST_64BIT
19291938

19301939
inline
19311940
size_t align_on_segment_hard_limit (size_t add)
@@ -7299,9 +7308,6 @@ bool gc_heap::virtual_commit (void* address, size_t size, int bucket, int h_numb
72997308
*
73007309
* Note : We never commit into free directly, so bucket != recorded_committed_free_bucket
73017310
*/
7302-
#ifndef HOST_64BIT
7303-
assert (heap_hard_limit == 0);
7304-
#endif //!HOST_64BIT
73057311

73067312
assert(0 <= bucket && bucket < recorded_committed_bucket_counts);
73077313
assert(bucket < total_oh_count || h_number == -1);
@@ -7444,9 +7450,6 @@ bool gc_heap::virtual_decommit (void* address, size_t size, int bucket, int h_nu
74447450
* Case 2: This is for bookkeeping - the bucket will be recorded_committed_bookkeeping_bucket, and the h_number will be -1
74457451
* Case 3: This is for free - the bucket will be recorded_committed_free_bucket, and the h_number will be -1
74467452
*/
7447-
#ifndef HOST_64BIT
7448-
assert (heap_hard_limit == 0);
7449-
#endif //!HOST_64BIT
74507453

74517454
bool decommit_succeeded_p = ((bucket != recorded_committed_bookkeeping_bucket) && use_large_pages_p) ? true : GCToOSInterface::VirtualDecommit (address, size);
74527455

@@ -14202,6 +14205,11 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1420214205
return E_OUTOFMEMORY;
1420314206
if (use_large_pages_p)
1420414207
{
14208+
#ifndef HOST_64BIT
14209+
// Large pages are not supported on 32bit
14210+
assert (false);
14211+
#endif //!HOST_64BIT
14212+
1420514213
if (heap_hard_limit_oh[soh])
1420614214
{
1420714215
heap_hard_limit_oh[soh] = soh_segment_size * number_of_heaps;
@@ -20815,12 +20823,12 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2081520823
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_before_oom);
2081620824
full_compact_gc_p = true;
2081720825
}
20818-
else if ((current_total_committed * 10) >= (heap_hard_limit * 9))
20826+
else if (((uint64_t)current_total_committed * (uint64_t)10) >= ((uint64_t)heap_hard_limit * (uint64_t)9))
2081920827
{
2082020828
size_t loh_frag = get_total_gen_fragmentation (loh_generation);
2082120829

2082220830
// If the LOH frag is >= 1/8 it's worth compacting it
20823-
if ((loh_frag * 8) >= heap_hard_limit)
20831+
if (loh_frag >= heap_hard_limit / 8)
2082420832
{
2082520833
dprintf (GTC_LOG, ("loh frag: %zd > 1/8 of limit %zd", loh_frag, (heap_hard_limit / 8)));
2082620834
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_frag);
@@ -20831,7 +20839,7 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2083120839
// If there's not much fragmentation but it looks like it'll be productive to
2083220840
// collect LOH, do that.
2083320841
size_t est_loh_reclaim = get_total_gen_estimated_reclaim (loh_generation);
20834-
if ((est_loh_reclaim * 8) >= heap_hard_limit)
20842+
if (est_loh_reclaim >= heap_hard_limit / 8)
2083520843
{
2083620844
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_reclaim);
2083720845
full_compact_gc_p = true;
@@ -43235,6 +43243,15 @@ void gc_heap::init_static_data()
4323543243
);
4323643244
#endif //MULTIPLE_HEAPS
4323743245

43246+
#ifndef HOST_64BIT
43247+
if (heap_hard_limit)
43248+
{
43249+
size_t gen1_max_size_seg = soh_segment_size / 2;
43250+
dprintf (GTC_LOG, ("limit gen1 max %zd->%zd", gen1_max_size, gen1_max_size_seg));
43251+
gen1_max_size = min (gen1_max_size, gen1_max_size_seg);
43252+
}
43253+
#endif //!HOST_64BIT
43254+
4323843255
size_t gen1_max_size_config = (size_t)GCConfig::GetGCGen1MaxBudget();
4323943256

4324043257
if (gen1_max_size_config)
@@ -48333,6 +48350,11 @@ HRESULT GCHeap::Initialize()
4833348350
{
4833448351
if (gc_heap::heap_hard_limit)
4833548352
{
48353+
#ifndef HOST_64BIT
48354+
// Regions are not supported on 32bit
48355+
assert(false);
48356+
#endif //!HOST_64BIT
48357+
4833648358
if (gc_heap::heap_hard_limit_oh[soh])
4833748359
{
4833848360
gc_heap::regions_range = gc_heap::heap_hard_limit;
@@ -48367,12 +48389,32 @@ HRESULT GCHeap::Initialize()
4836748389
{
4836848390
if (gc_heap::heap_hard_limit_oh[soh])
4836948391
{
48392+
// On 32bit we have next guarantees:
48393+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
48394+
// 0 <= (heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh]) < 4Gb (from gc_heap::compute_hard_limit_from_heap_limits)
48395+
// 0 <= heap_hard_limit_oh[loh] <= 1Gb or < 2Gb
48396+
// 0 <= heap_hard_limit_oh[poh] <= 1Gb or < 2Gb
48397+
// 0 <= large_seg_size <= 1Gb or <= 2Gb (alignment and round up)
48398+
// 0 <= pin_seg_size <= 1Gb or <= 2Gb (alignment and round up)
48399+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 4Gb
48400+
// 4Gb overflow is ok, because 0 size allocation will fail
4837048401
large_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[loh], nhp), seg_size_from_config);
4837148402
pin_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[poh], nhp), seg_size_from_config);
4837248403
}
4837348404
else
4837448405
{
48406+
// On 32bit we have next guarantees:
48407+
// 0 <= heap_hard_limit <= 1Gb (from gc_heap::compute_hard_limit)
48408+
// 0 <= soh_segment_size <= 1Gb
48409+
// 0 <= large_seg_size <= 1Gb
48410+
// 0 <= pin_seg_size <= 1Gb
48411+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 3Gb
48412+
#ifdef HOST_64BIT
4837548413
large_seg_size = gc_heap::use_large_pages_p ? gc_heap::soh_segment_size : gc_heap::soh_segment_size * 2;
48414+
#else //HOST_64BIT
48415+
assert (!gc_heap::use_large_pages_p);
48416+
large_seg_size = gc_heap::soh_segment_size;
48417+
#endif //HOST_64BIT
4837648418
pin_seg_size = large_seg_size;
4837748419
}
4837848420
if (gc_heap::use_large_pages_p)
@@ -52613,16 +52655,45 @@ int GCHeap::RefreshMemoryLimit()
5261352655
return gc_heap::refresh_memory_limit();
5261452656
}
5261552657

52658+
bool gc_heap::compute_hard_limit_from_heap_limits()
52659+
{
52660+
#ifndef HOST_64BIT
52661+
// need to consider overflows:
52662+
if (! ((heap_hard_limit_oh[soh] < max_heap_hard_limit && heap_hard_limit_oh[loh] <= max_heap_hard_limit / 2 && heap_hard_limit_oh[poh] <= max_heap_hard_limit / 2)
52663+
|| (heap_hard_limit_oh[soh] <= max_heap_hard_limit / 2 && heap_hard_limit_oh[loh] < max_heap_hard_limit && heap_hard_limit_oh[poh] <= max_heap_hard_limit / 2)
52664+
|| (heap_hard_limit_oh[soh] <= max_heap_hard_limit / 2 && heap_hard_limit_oh[loh] <= max_heap_hard_limit / 2 && heap_hard_limit_oh[poh] < max_heap_hard_limit)))
52665+
{
52666+
return false;
52667+
}
52668+
#endif //!HOST_64BIT
52669+
52670+
heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
52671+
return true;
52672+
}
52673+
52674+
// On 32bit we have next guarantees for limits:
52675+
// 1) heap-specific limits:
52676+
// 0 <= (heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh]) < 4Gb
52677+
// a) 0 <= heap_hard_limit_oh[soh] < 2Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
52678+
// b) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] < 2Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
52679+
// c) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] < 2Gb
52680+
// 2) same limit for all heaps:
52681+
// 0 <= heap_hard_limit <= 1Gb
52682+
//
52683+
// These ranges guarantee that calculation of soh_segment_size, loh_segment_size and poh_segment_size with alignment and round up won't overflow,
52684+
// as well as calculation of sum of them (overflow to 0 is allowed, because allocation with 0 size will fail later).
5261652685
bool gc_heap::compute_hard_limit()
5261752686
{
5261852687
heap_hard_limit_oh[soh] = 0;
52619-
#ifdef HOST_64BIT
52688+
5262052689
heap_hard_limit = (size_t)GCConfig::GetGCHeapHardLimit();
5262152690
heap_hard_limit_oh[soh] = (size_t)GCConfig::GetGCHeapHardLimitSOH();
5262252691
heap_hard_limit_oh[loh] = (size_t)GCConfig::GetGCHeapHardLimitLOH();
5262352692
heap_hard_limit_oh[poh] = (size_t)GCConfig::GetGCHeapHardLimitPOH();
5262452693

52694+
#ifdef HOST_64BIT
5262552695
use_large_pages_p = GCConfig::GetGCLargePages();
52696+
#endif //HOST_64BIT
5262652697

5262752698
if (heap_hard_limit_oh[soh] || heap_hard_limit_oh[loh] || heap_hard_limit_oh[poh])
5262852699
{
@@ -52634,8 +52705,10 @@ bool gc_heap::compute_hard_limit()
5263452705
{
5263552706
return false;
5263652707
}
52637-
heap_hard_limit = heap_hard_limit_oh[soh] +
52638-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
52708+
if (!compute_hard_limit_from_heap_limits())
52709+
{
52710+
return false;
52711+
}
5263952712
}
5264052713
else
5264152714
{
@@ -52663,9 +52736,22 @@ bool gc_heap::compute_hard_limit()
5266352736
heap_hard_limit_oh[soh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_soh / (uint64_t)100);
5266452737
heap_hard_limit_oh[loh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_loh / (uint64_t)100);
5266552738
heap_hard_limit_oh[poh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_poh / (uint64_t)100);
52666-
heap_hard_limit = heap_hard_limit_oh[soh] +
52667-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
52739+
52740+
if (!compute_hard_limit_from_heap_limits())
52741+
{
52742+
return false;
52743+
}
5266852744
}
52745+
#ifndef HOST_64BIT
52746+
else
52747+
{
52748+
// need to consider overflows
52749+
if (heap_hard_limit > max_heap_hard_limit / 2)
52750+
{
52751+
return false;
52752+
}
52753+
}
52754+
#endif //!HOST_64BIT
5266952755
}
5267052756

5267152757
if (heap_hard_limit_oh[soh] && (!heap_hard_limit_oh[poh]) && (!use_large_pages_p))
@@ -52679,9 +52765,17 @@ bool gc_heap::compute_hard_limit()
5267952765
if ((percent_of_mem > 0) && (percent_of_mem < 100))
5268052766
{
5268152767
heap_hard_limit = (size_t)(total_physical_mem * (uint64_t)percent_of_mem / (uint64_t)100);
52768+
52769+
#ifndef HOST_64BIT
52770+
// need to consider overflows
52771+
if (heap_hard_limit > max_heap_hard_limit / 2)
52772+
{
52773+
return false;
52774+
}
52775+
#endif //!HOST_64BIT
5268252776
}
5268352777
}
52684-
#endif //HOST_64BIT
52778+
5268552779
return true;
5268652780
}
5268752781

@@ -52706,12 +52800,12 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5270652800
}
5270752801
}
5270852802
}
52803+
#endif //HOST_64BIT
5270952804

5271052805
if (heap_hard_limit && (heap_hard_limit < new_current_total_committed))
5271152806
{
5271252807
return false;
5271352808
}
52714-
#endif //HOST_64BIT
5271552809

5271652810
#ifdef USE_REGIONS
5271752811
{
@@ -52730,9 +52824,24 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5273052824
seg_size_from_config = (size_t)GCConfig::GetSegmentSize();
5273152825
if (seg_size_from_config)
5273252826
{
52733-
seg_size_from_config = adjust_segment_size_hard_limit_va (seg_size_from_config);
52827+
seg_size_from_config = use_large_pages_p ? align_on_segment_hard_limit (seg_size_from_config) :
52828+
#ifdef HOST_64BIT
52829+
round_up_power2 (seg_size_from_config);
52830+
#else //HOST_64BIT
52831+
round_down_power2 (seg_size_from_config);
52832+
seg_size_from_config = min (seg_size_from_config, max_heap_hard_limit / 2);
52833+
#endif //HOST_64BIT
5273452834
}
5273552835

52836+
// On 32bit we have next guarantees:
52837+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
52838+
// a) heap-specific limits:
52839+
// 0 <= (heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh]) < 4Gb (from gc_heap::compute_hard_limit_from_heap_limits)
52840+
// 0 <= heap_hard_limit_oh[soh] <= 1Gb or < 2Gb
52841+
// 0 <= soh_segment_size <= 1Gb or <= 2Gb (alignment and round up)
52842+
// b) same limit for all heaps:
52843+
// 0 <= heap_hard_limit <= 1Gb
52844+
// 0 <= soh_segment_size <= 1Gb
5273652845
size_t limit_to_check = (heap_hard_limit_oh[soh] ? heap_hard_limit_oh[soh] : heap_hard_limit);
5273752846
soh_segment_size = max (adjust_segment_size_hard_limit (limit_to_check, nhp), seg_size_from_config);
5273852847
}

src/coreclr/gc/gcpriv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3363,6 +3363,8 @@ class gc_heap
33633363

33643364
PER_HEAP_ISOLATED_METHOD BOOL dt_high_memory_load_p();
33653365

3366+
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit_from_heap_limits();
3367+
33663368
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit();
33673369

33683370
PER_HEAP_ISOLATED_METHOD bool compute_memory_settings(bool is_initialization, uint32_t& nhp, uint32_t nhp_from_config, size_t& seg_size_from_config,

0 commit comments

Comments
 (0)