Skip to content

Commit 82d6a05

Browse files
committed
Add GC heap hard limit for 32 bit
1 parent f84d33c commit 82d6a05

File tree

2 files changed

+130
-23
lines changed

2 files changed

+130
-23
lines changed

src/coreclr/gc/gc.cpp

Lines changed: 128 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,7 +1919,16 @@ uint8_t* gc_heap::pad_for_alignment_large (uint8_t* newAlloc, int requiredAlignm
19191919
#endif //BACKGROUND_GC && !USE_REGIONS
19201920

19211921
// This is always power of 2.
1922+
#ifdef HOST_64BIT
19221923
const size_t min_segment_size_hard_limit = 1024*1024*16;
1924+
#else //HOST_64BIT
1925+
const size_t min_segment_size_hard_limit = 1024*1024*4;
1926+
#endif //HOST_64BIT
1927+
1928+
#ifndef HOST_64BIT
1929+
// Max size of heap hard limit (2^31) to be able to be aligned and rounded up on power of 2 and not overflow
1930+
const size_t max_heap_hard_limit = (size_t)2 * (size_t)1024 * (size_t)1024 * (size_t)1024;
1931+
#endif //!HOST_64BIT
19231932

19241933
inline
19251934
size_t align_on_segment_hard_limit (size_t add)
@@ -7278,10 +7287,6 @@ bool gc_heap::virtual_commit (void* address, size_t size, int bucket, int h_numb
72787287
*
72797288
* Note : We never commit into free directly, so bucket != recorded_committed_free_bucket
72807289
*/
7281-
#ifndef HOST_64BIT
7282-
assert (heap_hard_limit == 0);
7283-
#endif //!HOST_64BIT
7284-
72857290
assert(0 <= bucket && bucket < recorded_committed_bucket_counts);
72867291
assert(bucket < total_oh_count || h_number == -1);
72877292
assert(bucket != recorded_committed_free_bucket);
@@ -7385,10 +7390,6 @@ bool gc_heap::virtual_decommit (void* address, size_t size, int bucket, int h_nu
73857390
* Case 2: This is for bookkeeping - the bucket will be recorded_committed_bookkeeping_bucket, and the h_number will be -1
73867391
* Case 3: This is for free - the bucket will be recorded_committed_free_bucket, and the h_number will be -1
73877392
*/
7388-
#ifndef HOST_64BIT
7389-
assert (heap_hard_limit == 0);
7390-
#endif //!HOST_64BIT
7391-
73927393
assert(0 <= bucket && bucket < recorded_committed_bucket_counts);
73937394
assert(bucket < total_oh_count || h_number == -1);
73947395

@@ -14167,6 +14168,11 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1416714168
return E_OUTOFMEMORY;
1416814169
if (use_large_pages_p)
1416914170
{
14171+
#ifndef HOST_64BIT
14172+
// Large pages are not supported on 32bit
14173+
assert (false);
14174+
#endif //!HOST_64BIT
14175+
1417014176
if (heap_hard_limit_oh[soh])
1417114177
{
1417214178
heap_hard_limit_oh[soh] = soh_segment_size * number_of_heaps;
@@ -20774,12 +20780,12 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2077420780
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_before_oom);
2077520781
full_compact_gc_p = true;
2077620782
}
20777-
else if ((current_total_committed * 10) >= (heap_hard_limit * 9))
20783+
else if (((uint64_t)current_total_committed * (uint64_t)10) >= ((uint64_t)heap_hard_limit * (uint64_t)9))
2077820784
{
2077920785
size_t loh_frag = get_total_gen_fragmentation (loh_generation);
2078020786

2078120787
// If the LOH frag is >= 1/8 it's worth compacting it
20782-
if ((loh_frag * 8) >= heap_hard_limit)
20788+
if (loh_frag >= heap_hard_limit / 8)
2078320789
{
2078420790
dprintf (GTC_LOG, ("loh frag: %zd > 1/8 of limit %zd", loh_frag, (heap_hard_limit / 8)));
2078520791
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_frag);
@@ -20790,7 +20796,7 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2079020796
// If there's not much fragmentation but it looks like it'll be productive to
2079120797
// collect LOH, do that.
2079220798
size_t est_loh_reclaim = get_total_gen_estimated_reclaim (loh_generation);
20793-
if ((est_loh_reclaim * 8) >= heap_hard_limit)
20799+
if (est_loh_reclaim >= heap_hard_limit / 8)
2079420800
{
2079520801
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_reclaim);
2079620802
full_compact_gc_p = true;
@@ -24369,7 +24375,7 @@ heap_segment* gc_heap::unlink_first_rw_region (int gen_idx)
2436924375
assert (!heap_segment_read_only_p (region));
2437024376
dprintf (REGIONS_LOG, ("unlink_first_rw_region on heap: %d gen: %d region: %p", heap_number, gen_idx, heap_segment_mem (region)));
2437124377

24372-
#if defined(_DEBUG) && defined(HOST_64BIT)
24378+
#if defined(_DEBUG)
2437324379
#ifndef COMMITTED_BYTES_SHADOW
2437424380
if (heap_hard_limit)
2437524381
#endif //!COMMITTED_BYTES_SHADOW
@@ -24384,7 +24390,7 @@ heap_segment* gc_heap::unlink_first_rw_region (int gen_idx)
2438424390
g_heaps[old_heap]->committed_by_oh_per_heap[old_oh] -= committed;
2438524391
check_commit_cs.Leave();
2438624392
}
24387-
#endif // _DEBUG && HOST_64BIT
24393+
#endif // _DEBUG
2438824394

2438924395
set_heap_for_contained_basic_regions (region, nullptr);
2439024396

@@ -24412,7 +24418,7 @@ void gc_heap::thread_rw_region_front (int gen_idx, heap_segment* region)
2441224418
}
2441324419
dprintf (REGIONS_LOG, ("thread_rw_region_front on heap: %d gen: %d region: %p", heap_number, gen_idx, heap_segment_mem (region)));
2441424420

24415-
#if defined(_DEBUG) && defined(HOST_64BIT)
24421+
#if defined(_DEBUG)
2441624422
#ifndef COMMITTED_BYTES_SHADOW
2441724423
if (heap_hard_limit)
2441824424
#endif //!COMMITTED_BYTES_SHADOW
@@ -24427,7 +24433,7 @@ void gc_heap::thread_rw_region_front (int gen_idx, heap_segment* region)
2442724433
g_heaps[new_heap]->committed_by_oh_per_heap[new_oh] += committed;
2442824434
check_commit_cs.Leave();
2442924435
}
24430-
#endif // _DEBUG && HOST_64BIT
24436+
#endif // _DEBUG
2443124437

2443224438
set_heap_for_contained_basic_regions (region, this);
2443324439
}
@@ -43369,6 +43375,13 @@ void gc_heap::init_static_data()
4336943375
);
4337043376
#endif //MULTIPLE_HEAPS
4337143377

43378+
if (heap_hard_limit)
43379+
{
43380+
size_t gen1_max_size_seg = soh_segment_size / 2;
43381+
dprintf (GTC_LOG, ("limit gen1 max %zd->%zd", gen1_max_size, gen1_max_size_seg));
43382+
gen1_max_size = min (gen1_max_size, gen1_max_size_seg);
43383+
}
43384+
4337243385
size_t gen1_max_size_config = (size_t)GCConfig::GetGCGen1MaxBudget();
4337343386

4337443387
if (gen1_max_size_config)
@@ -48480,6 +48493,11 @@ HRESULT GCHeap::Initialize()
4848048493
{
4848148494
if (gc_heap::heap_hard_limit)
4848248495
{
48496+
#ifndef HOST_64BIT
48497+
// Regions are not supported on 32bit
48498+
assert(false);
48499+
#endif //!HOST_64BIT
48500+
4848348501
if (gc_heap::heap_hard_limit_oh[soh])
4848448502
{
4848548503
gc_heap::regions_range = gc_heap::heap_hard_limit;
@@ -48514,12 +48532,32 @@ HRESULT GCHeap::Initialize()
4851448532
{
4851548533
if (gc_heap::heap_hard_limit_oh[soh])
4851648534
{
48535+
// On 32bit we have next guarantees:
48536+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
48537+
// 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)
48538+
// 0 <= heap_hard_limit_oh[loh] <= 1Gb or < 2Gb
48539+
// 0 <= heap_hard_limit_oh[poh] <= 1Gb or < 2Gb
48540+
// 0 <= large_seg_size <= 1Gb or <= 2Gb (alignment and round up)
48541+
// 0 <= pin_seg_size <= 1Gb or <= 2Gb (alignment and round up)
48542+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 4Gb
48543+
// 4Gb overflow is ok, because 0 size allocation will fail
4851748544
large_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[loh], nhp), seg_size_from_config);
4851848545
pin_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[poh], nhp), seg_size_from_config);
4851948546
}
4852048547
else
4852148548
{
48549+
// On 32bit we have next guarantees:
48550+
// 0 <= heap_hard_limit <= 1Gb (from gc_heap::compute_hard_limit)
48551+
// 0 <= soh_segment_size <= 1Gb
48552+
// 0 <= large_seg_size <= 1Gb
48553+
// 0 <= pin_seg_size <= 1Gb
48554+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 3Gb
48555+
#ifdef HOST_64BIT
4852248556
large_seg_size = gc_heap::use_large_pages_p ? gc_heap::soh_segment_size : gc_heap::soh_segment_size * 2;
48557+
#else //HOST_64BIT
48558+
assert (!gc_heap::use_large_pages_p);
48559+
large_seg_size = gc_heap::soh_segment_size;
48560+
#endif //HOST_64BIT
4852348561
pin_seg_size = large_seg_size;
4852448562
}
4852548563
if (gc_heap::use_large_pages_p)
@@ -52683,16 +52721,45 @@ int GCHeap::RefreshMemoryLimit()
5268352721
return gc_heap::refresh_memory_limit();
5268452722
}
5268552723

52724+
bool gc_heap::compute_hard_limit_from_heap_limits()
52725+
{
52726+
#ifndef HOST_64BIT
52727+
// need to consider overflows:
52728+
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)
52729+
|| (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)
52730+
|| (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)))
52731+
{
52732+
return false;
52733+
}
52734+
#endif //!HOST_64BIT
52735+
52736+
heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
52737+
return true;
52738+
}
52739+
52740+
// On 32bit we have next guarantees for limits:
52741+
// 1) heap-specific limits:
52742+
// 0 <= (heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh]) < 4Gb
52743+
// a) 0 <= heap_hard_limit_oh[soh] < 2Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
52744+
// b) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] < 2Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
52745+
// c) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] < 2Gb
52746+
// 2) same limit for all heaps:
52747+
// 0 <= heap_hard_limit <= 1Gb
52748+
//
52749+
// These ranges guarantee that calculation of soh_segment_size, loh_segment_size and poh_segment_size with alignment and round up won't overflow,
52750+
// as well as calculation of sum of them (overflow to 0 is allowed, because allocation with 0 size will fail later).
5268652751
bool gc_heap::compute_hard_limit()
5268752752
{
5268852753
heap_hard_limit_oh[soh] = 0;
52689-
#ifdef HOST_64BIT
52754+
5269052755
heap_hard_limit = (size_t)GCConfig::GetGCHeapHardLimit();
5269152756
heap_hard_limit_oh[soh] = (size_t)GCConfig::GetGCHeapHardLimitSOH();
5269252757
heap_hard_limit_oh[loh] = (size_t)GCConfig::GetGCHeapHardLimitLOH();
5269352758
heap_hard_limit_oh[poh] = (size_t)GCConfig::GetGCHeapHardLimitPOH();
5269452759

52760+
#ifdef HOST_64BIT
5269552761
use_large_pages_p = GCConfig::GetGCLargePages();
52762+
#endif //HOST_64BIT
5269652763

5269752764
if (heap_hard_limit_oh[soh] || heap_hard_limit_oh[loh] || heap_hard_limit_oh[poh])
5269852765
{
@@ -52704,8 +52771,10 @@ bool gc_heap::compute_hard_limit()
5270452771
{
5270552772
return false;
5270652773
}
52707-
heap_hard_limit = heap_hard_limit_oh[soh] +
52708-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
52774+
if (!compute_hard_limit_from_heap_limits())
52775+
{
52776+
return false;
52777+
}
5270952778
}
5271052779
else
5271152780
{
@@ -52733,9 +52802,22 @@ bool gc_heap::compute_hard_limit()
5273352802
heap_hard_limit_oh[soh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_soh / (uint64_t)100);
5273452803
heap_hard_limit_oh[loh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_loh / (uint64_t)100);
5273552804
heap_hard_limit_oh[poh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_poh / (uint64_t)100);
52736-
heap_hard_limit = heap_hard_limit_oh[soh] +
52737-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
52805+
52806+
if (!compute_hard_limit_from_heap_limits())
52807+
{
52808+
return false;
52809+
}
52810+
}
52811+
#ifndef HOST_64BIT
52812+
else
52813+
{
52814+
// need to consider overflows
52815+
if (heap_hard_limit > max_heap_hard_limit / 2)
52816+
{
52817+
return false;
52818+
}
5273852819
}
52820+
#endif //!HOST_64BIT
5273952821
}
5274052822

5274152823
if (heap_hard_limit_oh[soh] && (!heap_hard_limit_oh[poh]) && (!use_large_pages_p))
@@ -52749,9 +52831,17 @@ bool gc_heap::compute_hard_limit()
5274952831
if ((percent_of_mem > 0) && (percent_of_mem < 100))
5275052832
{
5275152833
heap_hard_limit = (size_t)(total_physical_mem * (uint64_t)percent_of_mem / (uint64_t)100);
52834+
52835+
#ifndef HOST_64BIT
52836+
// need to consider overflows
52837+
if (heap_hard_limit > max_heap_hard_limit / 2)
52838+
{
52839+
return false;
52840+
}
52841+
#endif //!HOST_64BIT
5275252842
}
5275352843
}
52754-
#endif //HOST_64BIT
52844+
5275552845
return true;
5275652846
}
5275752847

@@ -52776,12 +52866,12 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5277652866
}
5277752867
}
5277852868
}
52869+
#endif //HOST_64BIT
5277952870

5278052871
if (heap_hard_limit && (heap_hard_limit < new_current_total_committed))
5278152872
{
5278252873
return false;
5278352874
}
52784-
#endif //HOST_64BIT
5278552875

5278652876
#ifdef USE_REGIONS
5278752877
{
@@ -52800,9 +52890,24 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5280052890
seg_size_from_config = (size_t)GCConfig::GetSegmentSize();
5280152891
if (seg_size_from_config)
5280252892
{
52803-
seg_size_from_config = adjust_segment_size_hard_limit_va (seg_size_from_config);
52893+
seg_size_from_config = use_large_pages_p ? align_on_segment_hard_limit (seg_size_from_config) :
52894+
#ifdef HOST_64BIT
52895+
round_up_power2 (seg_size_from_config);
52896+
#else //HOST_64BIT
52897+
round_down_power2 (seg_size_from_config);
52898+
seg_size_from_config = min (seg_size_from_config, max_heap_hard_limit / 2);
52899+
#endif //HOST_64BIT
5280452900
}
5280552901

52902+
// On 32bit we have next guarantees:
52903+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
52904+
// a) heap-specific limits:
52905+
// 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)
52906+
// 0 <= heap_hard_limit_oh[soh] <= 1Gb or < 2Gb
52907+
// 0 <= soh_segment_size <= 1Gb or <= 2Gb (alignment and round up)
52908+
// b) same limit for all heaps:
52909+
// 0 <= heap_hard_limit <= 1Gb
52910+
// 0 <= soh_segment_size <= 1Gb
5280652911
size_t limit_to_check = (heap_hard_limit_oh[soh] ? heap_hard_limit_oh[soh] : heap_hard_limit);
5280752912
soh_segment_size = max (adjust_segment_size_hard_limit (limit_to_check, nhp), seg_size_from_config);
5280852913
}

src/coreclr/gc/gcpriv.h

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

33403340
PER_HEAP_ISOLATED_METHOD BOOL dt_high_memory_load_p();
33413341

3342+
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit_from_heap_limits();
3343+
33423344
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit();
33433345

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