Skip to content

Commit 21708c7

Browse files
committed
Add GC heap hard limit for 32 bit
1 parent 8b1d30a commit 21708c7

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
@@ -1968,7 +1968,16 @@ uint8_t* gc_heap::pad_for_alignment_large (uint8_t* newAlloc, int requiredAlignm
19681968
#endif //BACKGROUND_GC && !USE_REGIONS
19691969

19701970
// This is always power of 2.
1971+
#ifdef HOST_64BIT
19711972
const size_t min_segment_size_hard_limit = 1024*1024*16;
1973+
#else //HOST_64BIT
1974+
const size_t min_segment_size_hard_limit = 1024*1024*4;
1975+
#endif //HOST_64BIT
1976+
1977+
#ifndef HOST_64BIT
1978+
// Max size of heap hard limit (2^31) to be able to be aligned and rounded up on power of 2 and not overflow
1979+
const size_t max_heap_hard_limit = (size_t)2 * (size_t)1024 * (size_t)1024 * (size_t)1024;
1980+
#endif //!HOST_64BIT
19721981

19731982
inline
19741983
size_t align_on_segment_hard_limit (size_t add)
@@ -7405,9 +7414,6 @@ bool gc_heap::virtual_commit (void* address, size_t size, int bucket, int h_numb
74057414
*
74067415
* Note : We never commit into free directly, so bucket != recorded_committed_free_bucket
74077416
*/
7408-
#ifndef HOST_64BIT
7409-
assert (heap_hard_limit == 0);
7410-
#endif //!HOST_64BIT
74117417

74127418
assert(0 <= bucket && bucket < recorded_committed_bucket_counts);
74137419
assert(bucket < total_oh_count || h_number == -1);
@@ -7550,9 +7556,6 @@ bool gc_heap::virtual_decommit (void* address, size_t size, int bucket, int h_nu
75507556
* Case 2: This is for bookkeeping - the bucket will be recorded_committed_bookkeeping_bucket, and the h_number will be -1
75517557
* Case 3: This is for free - the bucket will be recorded_committed_free_bucket, and the h_number will be -1
75527558
*/
7553-
#ifndef HOST_64BIT
7554-
assert (heap_hard_limit == 0);
7555-
#endif //!HOST_64BIT
75567559

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

@@ -14347,6 +14350,11 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1434714350
return E_OUTOFMEMORY;
1434814351
if (use_large_pages_p)
1434914352
{
14353+
#ifndef HOST_64BIT
14354+
// Large pages are not supported on 32bit
14355+
assert (false);
14356+
#endif //!HOST_64BIT
14357+
1435014358
if (heap_hard_limit_oh[soh])
1435114359
{
1435214360
heap_hard_limit_oh[soh] = soh_segment_size * number_of_heaps;
@@ -20975,12 +20983,12 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2097520983
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_before_oom);
2097620984
full_compact_gc_p = true;
2097720985
}
20978-
else if ((current_total_committed * 10) >= (heap_hard_limit * 9))
20986+
else if (((uint64_t)current_total_committed * (uint64_t)10) >= ((uint64_t)heap_hard_limit * (uint64_t)9))
2097920987
{
2098020988
size_t loh_frag = get_total_gen_fragmentation (loh_generation);
2098120989

2098220990
// If the LOH frag is >= 1/8 it's worth compacting it
20983-
if ((loh_frag * 8) >= heap_hard_limit)
20991+
if (loh_frag >= heap_hard_limit / 8)
2098420992
{
2098520993
dprintf (GTC_LOG, ("loh frag: %zd > 1/8 of limit %zd", loh_frag, (heap_hard_limit / 8)));
2098620994
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_frag);
@@ -20991,7 +20999,7 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2099120999
// If there's not much fragmentation but it looks like it'll be productive to
2099221000
// collect LOH, do that.
2099321001
size_t est_loh_reclaim = get_total_gen_estimated_reclaim (loh_generation);
20994-
if ((est_loh_reclaim * 8) >= heap_hard_limit)
21002+
if (est_loh_reclaim >= heap_hard_limit / 8)
2099521003
{
2099621004
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_reclaim);
2099721005
full_compact_gc_p = true;
@@ -43743,6 +43751,15 @@ void gc_heap::init_static_data()
4374343751
);
4374443752
#endif //MULTIPLE_HEAPS
4374543753

43754+
#ifndef HOST_64BIT
43755+
if (heap_hard_limit)
43756+
{
43757+
size_t gen1_max_size_seg = soh_segment_size / 2;
43758+
dprintf (GTC_LOG, ("limit gen1 max %zd->%zd", gen1_max_size, gen1_max_size_seg));
43759+
gen1_max_size = min (gen1_max_size, gen1_max_size_seg);
43760+
}
43761+
#endif //!HOST_64BIT
43762+
4374643763
size_t gen1_max_size_config = (size_t)GCConfig::GetGCGen1MaxBudget();
4374743764

4374843765
if (gen1_max_size_config)
@@ -48868,6 +48885,11 @@ HRESULT GCHeap::Initialize()
4886848885
{
4886948886
if (gc_heap::heap_hard_limit)
4887048887
{
48888+
#ifndef HOST_64BIT
48889+
// Regions are not supported on 32bit
48890+
assert(false);
48891+
#endif //!HOST_64BIT
48892+
4887148893
if (gc_heap::heap_hard_limit_oh[soh])
4887248894
{
4887348895
gc_heap::regions_range = gc_heap::heap_hard_limit;
@@ -48902,12 +48924,32 @@ HRESULT GCHeap::Initialize()
4890248924
{
4890348925
if (gc_heap::heap_hard_limit_oh[soh])
4890448926
{
48927+
// On 32bit we have next guarantees:
48928+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
48929+
// 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)
48930+
// 0 <= heap_hard_limit_oh[loh] <= 1Gb or < 2Gb
48931+
// 0 <= heap_hard_limit_oh[poh] <= 1Gb or < 2Gb
48932+
// 0 <= large_seg_size <= 1Gb or <= 2Gb (alignment and round up)
48933+
// 0 <= pin_seg_size <= 1Gb or <= 2Gb (alignment and round up)
48934+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 4Gb
48935+
// 4Gb overflow is ok, because 0 size allocation will fail
4890548936
large_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[loh], nhp), seg_size_from_config);
4890648937
pin_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[poh], nhp), seg_size_from_config);
4890748938
}
4890848939
else
4890948940
{
48941+
// On 32bit we have next guarantees:
48942+
// 0 <= heap_hard_limit <= 1Gb (from gc_heap::compute_hard_limit)
48943+
// 0 <= soh_segment_size <= 1Gb
48944+
// 0 <= large_seg_size <= 1Gb
48945+
// 0 <= pin_seg_size <= 1Gb
48946+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 3Gb
48947+
#ifdef HOST_64BIT
4891048948
large_seg_size = gc_heap::use_large_pages_p ? gc_heap::soh_segment_size : gc_heap::soh_segment_size * 2;
48949+
#else //HOST_64BIT
48950+
assert (!gc_heap::use_large_pages_p);
48951+
large_seg_size = gc_heap::soh_segment_size;
48952+
#endif //HOST_64BIT
4891148953
pin_seg_size = large_seg_size;
4891248954
}
4891348955
if (gc_heap::use_large_pages_p)
@@ -53173,16 +53215,45 @@ int GCHeap::RefreshMemoryLimit()
5317353215
return gc_heap::refresh_memory_limit();
5317453216
}
5317553217

53218+
bool gc_heap::compute_hard_limit_from_heap_limits()
53219+
{
53220+
#ifndef HOST_64BIT
53221+
// need to consider overflows:
53222+
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)
53223+
|| (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)
53224+
|| (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)))
53225+
{
53226+
return false;
53227+
}
53228+
#endif //!HOST_64BIT
53229+
53230+
heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
53231+
return true;
53232+
}
53233+
53234+
// On 32bit we have next guarantees for limits:
53235+
// 1) heap-specific limits:
53236+
// 0 <= (heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh]) < 4Gb
53237+
// a) 0 <= heap_hard_limit_oh[soh] < 2Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
53238+
// b) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] < 2Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
53239+
// c) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] < 2Gb
53240+
// 2) same limit for all heaps:
53241+
// 0 <= heap_hard_limit <= 1Gb
53242+
//
53243+
// These ranges guarantee that calculation of soh_segment_size, loh_segment_size and poh_segment_size with alignment and round up won't overflow,
53244+
// as well as calculation of sum of them (overflow to 0 is allowed, because allocation with 0 size will fail later).
5317653245
bool gc_heap::compute_hard_limit()
5317753246
{
5317853247
heap_hard_limit_oh[soh] = 0;
53179-
#ifdef HOST_64BIT
53248+
5318053249
heap_hard_limit = (size_t)GCConfig::GetGCHeapHardLimit();
5318153250
heap_hard_limit_oh[soh] = (size_t)GCConfig::GetGCHeapHardLimitSOH();
5318253251
heap_hard_limit_oh[loh] = (size_t)GCConfig::GetGCHeapHardLimitLOH();
5318353252
heap_hard_limit_oh[poh] = (size_t)GCConfig::GetGCHeapHardLimitPOH();
5318453253

53254+
#ifdef HOST_64BIT
5318553255
use_large_pages_p = GCConfig::GetGCLargePages();
53256+
#endif //HOST_64BIT
5318653257

5318753258
if (heap_hard_limit_oh[soh] || heap_hard_limit_oh[loh] || heap_hard_limit_oh[poh])
5318853259
{
@@ -53194,8 +53265,10 @@ bool gc_heap::compute_hard_limit()
5319453265
{
5319553266
return false;
5319653267
}
53197-
heap_hard_limit = heap_hard_limit_oh[soh] +
53198-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
53268+
if (!compute_hard_limit_from_heap_limits())
53269+
{
53270+
return false;
53271+
}
5319953272
}
5320053273
else
5320153274
{
@@ -53223,9 +53296,22 @@ bool gc_heap::compute_hard_limit()
5322353296
heap_hard_limit_oh[soh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_soh / (uint64_t)100);
5322453297
heap_hard_limit_oh[loh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_loh / (uint64_t)100);
5322553298
heap_hard_limit_oh[poh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_poh / (uint64_t)100);
53226-
heap_hard_limit = heap_hard_limit_oh[soh] +
53227-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
53299+
53300+
if (!compute_hard_limit_from_heap_limits())
53301+
{
53302+
return false;
53303+
}
5322853304
}
53305+
#ifndef HOST_64BIT
53306+
else
53307+
{
53308+
// need to consider overflows
53309+
if (heap_hard_limit > max_heap_hard_limit / 2)
53310+
{
53311+
return false;
53312+
}
53313+
}
53314+
#endif //!HOST_64BIT
5322953315
}
5323053316

5323153317
if (heap_hard_limit_oh[soh] && (!heap_hard_limit_oh[poh]) && (!use_large_pages_p))
@@ -53239,9 +53325,17 @@ bool gc_heap::compute_hard_limit()
5323953325
if ((percent_of_mem > 0) && (percent_of_mem < 100))
5324053326
{
5324153327
heap_hard_limit = (size_t)(total_physical_mem * (uint64_t)percent_of_mem / (uint64_t)100);
53328+
53329+
#ifndef HOST_64BIT
53330+
// need to consider overflows
53331+
if (heap_hard_limit > max_heap_hard_limit / 2)
53332+
{
53333+
return false;
53334+
}
53335+
#endif //!HOST_64BIT
5324253336
}
5324353337
}
53244-
#endif //HOST_64BIT
53338+
5324553339
return true;
5324653340
}
5324753341

@@ -53266,12 +53360,12 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5326653360
}
5326753361
}
5326853362
}
53363+
#endif //HOST_64BIT
5326953364

5327053365
if (heap_hard_limit && (heap_hard_limit < new_current_total_committed))
5327153366
{
5327253367
return false;
5327353368
}
53274-
#endif //HOST_64BIT
5327553369

5327653370
#ifdef USE_REGIONS
5327753371
{
@@ -53290,9 +53384,24 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5329053384
seg_size_from_config = (size_t)GCConfig::GetSegmentSize();
5329153385
if (seg_size_from_config)
5329253386
{
53293-
seg_size_from_config = adjust_segment_size_hard_limit_va (seg_size_from_config);
53387+
seg_size_from_config = use_large_pages_p ? align_on_segment_hard_limit (seg_size_from_config) :
53388+
#ifdef HOST_64BIT
53389+
round_up_power2 (seg_size_from_config);
53390+
#else //HOST_64BIT
53391+
round_down_power2 (seg_size_from_config);
53392+
seg_size_from_config = min (seg_size_from_config, max_heap_hard_limit / 2);
53393+
#endif //HOST_64BIT
5329453394
}
5329553395

53396+
// On 32bit we have next guarantees:
53397+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
53398+
// a) heap-specific limits:
53399+
// 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)
53400+
// 0 <= heap_hard_limit_oh[soh] <= 1Gb or < 2Gb
53401+
// 0 <= soh_segment_size <= 1Gb or <= 2Gb (alignment and round up)
53402+
// b) same limit for all heaps:
53403+
// 0 <= heap_hard_limit <= 1Gb
53404+
// 0 <= soh_segment_size <= 1Gb
5329653405
size_t limit_to_check = (heap_hard_limit_oh[soh] ? heap_hard_limit_oh[soh] : heap_hard_limit);
5329753406
soh_segment_size = max (adjust_segment_size_hard_limit (limit_to_check, nhp), seg_size_from_config);
5329853407
}

src/coreclr/gc/gcpriv.h

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

34393439
PER_HEAP_ISOLATED_METHOD BOOL dt_high_memory_load_p();
34403440

3441+
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit_from_heap_limits();
3442+
34413443
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit();
34423444

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