Skip to content

Commit 6730a10

Browse files
gbalykovVSadov
authored andcommitted
Add GC heap hard limit for 32 bit
1 parent 96acc3c commit 6730a10

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

19911991
// This is always power of 2.
1992+
#ifdef HOST_64BIT
19921993
const size_t min_segment_size_hard_limit = 1024*1024*16;
1994+
#else //HOST_64BIT
1995+
const size_t min_segment_size_hard_limit = 1024*1024*4;
1996+
#endif //HOST_64BIT
1997+
1998+
#ifndef HOST_64BIT
1999+
// Max size of heap hard limit (2^31) to be able to be aligned and rounded up on power of 2 and not overflow
2000+
const size_t max_heap_hard_limit = (size_t)2 * (size_t)1024 * (size_t)1024 * (size_t)1024;
2001+
#endif //!HOST_64BIT
19932002

19942003
inline
19952004
size_t align_on_segment_hard_limit (size_t add)
@@ -7442,9 +7451,6 @@ bool gc_heap::virtual_commit (void* address, size_t size, int bucket, int h_numb
74427451
*
74437452
* Note : We never commit into free directly, so bucket != recorded_committed_free_bucket
74447453
*/
7445-
#ifndef HOST_64BIT
7446-
assert (heap_hard_limit == 0);
7447-
#endif //!HOST_64BIT
74487454

74497455
assert(0 <= bucket && bucket < recorded_committed_bucket_counts);
74507456
assert(bucket < total_oh_count || h_number == -1);
@@ -7587,9 +7593,6 @@ bool gc_heap::virtual_decommit (void* address, size_t size, int bucket, int h_nu
75877593
* Case 2: This is for bookkeeping - the bucket will be recorded_committed_bookkeeping_bucket, and the h_number will be -1
75887594
* Case 3: This is for free - the bucket will be recorded_committed_free_bucket, and the h_number will be -1
75897595
*/
7590-
#ifndef HOST_64BIT
7591-
assert (heap_hard_limit == 0);
7592-
#endif //!HOST_64BIT
75937596

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

@@ -14488,6 +14491,11 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1448814491
return E_OUTOFMEMORY;
1448914492
if (use_large_pages_p)
1449014493
{
14494+
#ifndef HOST_64BIT
14495+
// Large pages are not supported on 32bit
14496+
assert (false);
14497+
#endif //!HOST_64BIT
14498+
1449114499
if (heap_hard_limit_oh[soh])
1449214500
{
1449314501
heap_hard_limit_oh[soh] = soh_segment_size * number_of_heaps;
@@ -21128,12 +21136,12 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2112821136
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_before_oom);
2112921137
full_compact_gc_p = true;
2113021138
}
21131-
else if ((current_total_committed * 10) >= (heap_hard_limit * 9))
21139+
else if (((uint64_t)current_total_committed * (uint64_t)10) >= ((uint64_t)heap_hard_limit * (uint64_t)9))
2113221140
{
2113321141
size_t loh_frag = get_total_gen_fragmentation (loh_generation);
2113421142

2113521143
// If the LOH frag is >= 1/8 it's worth compacting it
21136-
if ((loh_frag * 8) >= heap_hard_limit)
21144+
if (loh_frag >= heap_hard_limit / 8)
2113721145
{
2113821146
dprintf (GTC_LOG, ("loh frag: %zd > 1/8 of limit %zd", loh_frag, (heap_hard_limit / 8)));
2113921147
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_frag);
@@ -21144,7 +21152,7 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2114421152
// If there's not much fragmentation but it looks like it'll be productive to
2114521153
// collect LOH, do that.
2114621154
size_t est_loh_reclaim = get_total_gen_estimated_reclaim (loh_generation);
21147-
if ((est_loh_reclaim * 8) >= heap_hard_limit)
21155+
if (est_loh_reclaim >= heap_hard_limit / 8)
2114821156
{
2114921157
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_reclaim);
2115021158
full_compact_gc_p = true;
@@ -44159,6 +44167,15 @@ void gc_heap::init_static_data()
4415944167
);
4416044168
#endif //MULTIPLE_HEAPS
4416144169

44170+
#ifndef HOST_64BIT
44171+
if (heap_hard_limit)
44172+
{
44173+
size_t gen1_max_size_seg = soh_segment_size / 2;
44174+
dprintf (GTC_LOG, ("limit gen1 max %zd->%zd", gen1_max_size, gen1_max_size_seg));
44175+
gen1_max_size = min (gen1_max_size, gen1_max_size_seg);
44176+
}
44177+
#endif //!HOST_64BIT
44178+
4416244179
size_t gen1_max_size_config = (size_t)GCConfig::GetGCGen1MaxBudget();
4416344180

4416444181
if (gen1_max_size_config)
@@ -49291,6 +49308,11 @@ HRESULT GCHeap::Initialize()
4929149308
{
4929249309
if (gc_heap::heap_hard_limit)
4929349310
{
49311+
#ifndef HOST_64BIT
49312+
// Regions are not supported on 32bit
49313+
assert(false);
49314+
#endif //!HOST_64BIT
49315+
4929449316
if (gc_heap::heap_hard_limit_oh[soh])
4929549317
{
4929649318
gc_heap::regions_range = gc_heap::heap_hard_limit;
@@ -49332,12 +49354,32 @@ HRESULT GCHeap::Initialize()
4933249354
{
4933349355
if (gc_heap::heap_hard_limit_oh[soh])
4933449356
{
49357+
// On 32bit we have next guarantees:
49358+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
49359+
// 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)
49360+
// 0 <= heap_hard_limit_oh[loh] <= 1Gb or < 2Gb
49361+
// 0 <= heap_hard_limit_oh[poh] <= 1Gb or < 2Gb
49362+
// 0 <= large_seg_size <= 1Gb or <= 2Gb (alignment and round up)
49363+
// 0 <= pin_seg_size <= 1Gb or <= 2Gb (alignment and round up)
49364+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 4Gb
49365+
// 4Gb overflow is ok, because 0 size allocation will fail
4933549366
large_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[loh], nhp), seg_size_from_config);
4933649367
pin_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[poh], nhp), seg_size_from_config);
4933749368
}
4933849369
else
4933949370
{
49371+
// On 32bit we have next guarantees:
49372+
// 0 <= heap_hard_limit <= 1Gb (from gc_heap::compute_hard_limit)
49373+
// 0 <= soh_segment_size <= 1Gb
49374+
// 0 <= large_seg_size <= 1Gb
49375+
// 0 <= pin_seg_size <= 1Gb
49376+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 3Gb
49377+
#ifdef HOST_64BIT
4934049378
large_seg_size = gc_heap::use_large_pages_p ? gc_heap::soh_segment_size : gc_heap::soh_segment_size * 2;
49379+
#else //HOST_64BIT
49380+
assert (!gc_heap::use_large_pages_p);
49381+
large_seg_size = gc_heap::soh_segment_size;
49382+
#endif //HOST_64BIT
4934149383
pin_seg_size = large_seg_size;
4934249384
}
4934349385
if (gc_heap::use_large_pages_p)
@@ -53679,16 +53721,45 @@ int GCHeap::RefreshMemoryLimit()
5367953721
return gc_heap::refresh_memory_limit();
5368053722
}
5368153723

53724+
bool gc_heap::compute_hard_limit_from_heap_limits()
53725+
{
53726+
#ifndef HOST_64BIT
53727+
// need to consider overflows:
53728+
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)
53729+
|| (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)
53730+
|| (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)))
53731+
{
53732+
return false;
53733+
}
53734+
#endif //!HOST_64BIT
53735+
53736+
heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
53737+
return true;
53738+
}
53739+
53740+
// On 32bit we have next guarantees for limits:
53741+
// 1) heap-specific limits:
53742+
// 0 <= (heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh]) < 4Gb
53743+
// a) 0 <= heap_hard_limit_oh[soh] < 2Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
53744+
// b) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] < 2Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
53745+
// c) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] < 2Gb
53746+
// 2) same limit for all heaps:
53747+
// 0 <= heap_hard_limit <= 1Gb
53748+
//
53749+
// These ranges guarantee that calculation of soh_segment_size, loh_segment_size and poh_segment_size with alignment and round up won't overflow,
53750+
// as well as calculation of sum of them (overflow to 0 is allowed, because allocation with 0 size will fail later).
5368253751
bool gc_heap::compute_hard_limit()
5368353752
{
5368453753
heap_hard_limit_oh[soh] = 0;
53685-
#ifdef HOST_64BIT
53754+
5368653755
heap_hard_limit = (size_t)GCConfig::GetGCHeapHardLimit();
5368753756
heap_hard_limit_oh[soh] = (size_t)GCConfig::GetGCHeapHardLimitSOH();
5368853757
heap_hard_limit_oh[loh] = (size_t)GCConfig::GetGCHeapHardLimitLOH();
5368953758
heap_hard_limit_oh[poh] = (size_t)GCConfig::GetGCHeapHardLimitPOH();
5369053759

53760+
#ifdef HOST_64BIT
5369153761
use_large_pages_p = GCConfig::GetGCLargePages();
53762+
#endif //HOST_64BIT
5369253763

5369353764
if (heap_hard_limit_oh[soh] || heap_hard_limit_oh[loh] || heap_hard_limit_oh[poh])
5369453765
{
@@ -53700,8 +53771,10 @@ bool gc_heap::compute_hard_limit()
5370053771
{
5370153772
return false;
5370253773
}
53703-
heap_hard_limit = heap_hard_limit_oh[soh] +
53704-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
53774+
if (!compute_hard_limit_from_heap_limits())
53775+
{
53776+
return false;
53777+
}
5370553778
}
5370653779
else
5370753780
{
@@ -53729,9 +53802,22 @@ bool gc_heap::compute_hard_limit()
5372953802
heap_hard_limit_oh[soh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_soh / (uint64_t)100);
5373053803
heap_hard_limit_oh[loh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_loh / (uint64_t)100);
5373153804
heap_hard_limit_oh[poh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_poh / (uint64_t)100);
53732-
heap_hard_limit = heap_hard_limit_oh[soh] +
53733-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
53805+
53806+
if (!compute_hard_limit_from_heap_limits())
53807+
{
53808+
return false;
53809+
}
5373453810
}
53811+
#ifndef HOST_64BIT
53812+
else
53813+
{
53814+
// need to consider overflows
53815+
if (heap_hard_limit > max_heap_hard_limit / 2)
53816+
{
53817+
return false;
53818+
}
53819+
}
53820+
#endif //!HOST_64BIT
5373553821
}
5373653822

5373753823
if (heap_hard_limit_oh[soh] && (!heap_hard_limit_oh[poh]) && (!use_large_pages_p))
@@ -53745,9 +53831,17 @@ bool gc_heap::compute_hard_limit()
5374553831
if ((percent_of_mem > 0) && (percent_of_mem < 100))
5374653832
{
5374753833
heap_hard_limit = (size_t)(total_physical_mem * (uint64_t)percent_of_mem / (uint64_t)100);
53834+
53835+
#ifndef HOST_64BIT
53836+
// need to consider overflows
53837+
if (heap_hard_limit > max_heap_hard_limit / 2)
53838+
{
53839+
return false;
53840+
}
53841+
#endif //!HOST_64BIT
5374853842
}
5374953843
}
53750-
#endif //HOST_64BIT
53844+
5375153845
return true;
5375253846
}
5375353847

@@ -53772,12 +53866,12 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5377253866
}
5377353867
}
5377453868
}
53869+
#endif //HOST_64BIT
5377553870

5377653871
if (heap_hard_limit && (heap_hard_limit < new_current_total_committed))
5377753872
{
5377853873
return false;
5377953874
}
53780-
#endif //HOST_64BIT
5378153875

5378253876
#ifdef USE_REGIONS
5378353877
{
@@ -53796,9 +53890,24 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5379653890
seg_size_from_config = (size_t)GCConfig::GetSegmentSize();
5379753891
if (seg_size_from_config)
5379853892
{
53799-
seg_size_from_config = adjust_segment_size_hard_limit_va (seg_size_from_config);
53893+
seg_size_from_config = use_large_pages_p ? align_on_segment_hard_limit (seg_size_from_config) :
53894+
#ifdef HOST_64BIT
53895+
round_up_power2 (seg_size_from_config);
53896+
#else //HOST_64BIT
53897+
round_down_power2 (seg_size_from_config);
53898+
seg_size_from_config = min (seg_size_from_config, max_heap_hard_limit / 2);
53899+
#endif //HOST_64BIT
5380053900
}
5380153901

53902+
// On 32bit we have next guarantees:
53903+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
53904+
// a) heap-specific limits:
53905+
// 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)
53906+
// 0 <= heap_hard_limit_oh[soh] <= 1Gb or < 2Gb
53907+
// 0 <= soh_segment_size <= 1Gb or <= 2Gb (alignment and round up)
53908+
// b) same limit for all heaps:
53909+
// 0 <= heap_hard_limit <= 1Gb
53910+
// 0 <= soh_segment_size <= 1Gb
5380253911
size_t limit_to_check = (heap_hard_limit_oh[soh] ? heap_hard_limit_oh[soh] : heap_hard_limit);
5380353912
soh_segment_size = max (adjust_segment_size_hard_limit (limit_to_check, nhp), seg_size_from_config);
5380453913
}

src/coreclr/gc/gcpriv.h

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

34773477
PER_HEAP_ISOLATED_METHOD BOOL dt_high_memory_load_p();
34783478

3479+
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit_from_heap_limits();
3480+
34793481
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit();
34803482

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