Skip to content

Commit 56db53a

Browse files
committed
Add GC heap hard limit for 32 bit
1 parent c87cbf6 commit 56db53a

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)
@@ -7336,9 +7345,6 @@ bool gc_heap::virtual_commit (void* address, size_t size, int bucket, int h_numb
73367345
*
73377346
* Note : We never commit into free directly, so bucket != recorded_committed_free_bucket
73387347
*/
7339-
#ifndef HOST_64BIT
7340-
assert (heap_hard_limit == 0);
7341-
#endif //!HOST_64BIT
73427348

73437349
assert(0 <= bucket && bucket < recorded_committed_bucket_counts);
73447350
assert(bucket < total_oh_count || h_number == -1);
@@ -7481,9 +7487,6 @@ bool gc_heap::virtual_decommit (void* address, size_t size, int bucket, int h_nu
74817487
* Case 2: This is for bookkeeping - the bucket will be recorded_committed_bookkeeping_bucket, and the h_number will be -1
74827488
* Case 3: This is for free - the bucket will be recorded_committed_free_bucket, and the h_number will be -1
74837489
*/
7484-
#ifndef HOST_64BIT
7485-
assert (heap_hard_limit == 0);
7486-
#endif //!HOST_64BIT
74877490

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

@@ -14301,6 +14304,11 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size,
1430114304
return E_OUTOFMEMORY;
1430214305
if (use_large_pages_p)
1430314306
{
14307+
#ifndef HOST_64BIT
14308+
// Large pages are not supported on 32bit
14309+
assert (false);
14310+
#endif //!HOST_64BIT
14311+
1430414312
if (heap_hard_limit_oh[soh])
1430514313
{
1430614314
heap_hard_limit_oh[soh] = soh_segment_size * number_of_heaps;
@@ -20921,12 +20929,12 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2092120929
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_before_oom);
2092220930
full_compact_gc_p = true;
2092320931
}
20924-
else if ((current_total_committed * 10) >= (heap_hard_limit * 9))
20932+
else if (((uint64_t)current_total_committed * (uint64_t)10) >= ((uint64_t)heap_hard_limit * (uint64_t)9))
2092520933
{
2092620934
size_t loh_frag = get_total_gen_fragmentation (loh_generation);
2092720935

2092820936
// If the LOH frag is >= 1/8 it's worth compacting it
20929-
if ((loh_frag * 8) >= heap_hard_limit)
20937+
if (loh_frag >= heap_hard_limit / 8)
2093020938
{
2093120939
dprintf (GTC_LOG, ("loh frag: %zd > 1/8 of limit %zd", loh_frag, (heap_hard_limit / 8)));
2093220940
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_frag);
@@ -20937,7 +20945,7 @@ int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
2093720945
// If there's not much fragmentation but it looks like it'll be productive to
2093820946
// collect LOH, do that.
2093920947
size_t est_loh_reclaim = get_total_gen_estimated_reclaim (loh_generation);
20940-
if ((est_loh_reclaim * 8) >= heap_hard_limit)
20948+
if (est_loh_reclaim >= heap_hard_limit / 8)
2094120949
{
2094220950
gc_data_global.gen_to_condemn_reasons.set_condition(gen_joined_limit_loh_reclaim);
2094320951
full_compact_gc_p = true;
@@ -43556,6 +43564,15 @@ void gc_heap::init_static_data()
4355643564
);
4355743565
#endif //MULTIPLE_HEAPS
4355843566

43567+
#ifndef HOST_64BIT
43568+
if (heap_hard_limit)
43569+
{
43570+
size_t gen1_max_size_seg = soh_segment_size / 2;
43571+
dprintf (GTC_LOG, ("limit gen1 max %zd->%zd", gen1_max_size, gen1_max_size_seg));
43572+
gen1_max_size = min (gen1_max_size, gen1_max_size_seg);
43573+
}
43574+
#endif //!HOST_64BIT
43575+
4355943576
size_t gen1_max_size_config = (size_t)GCConfig::GetGCGen1MaxBudget();
4356043577

4356143578
if (gen1_max_size_config)
@@ -48681,6 +48698,11 @@ HRESULT GCHeap::Initialize()
4868148698
{
4868248699
if (gc_heap::heap_hard_limit)
4868348700
{
48701+
#ifndef HOST_64BIT
48702+
// Regions are not supported on 32bit
48703+
assert(false);
48704+
#endif //!HOST_64BIT
48705+
4868448706
if (gc_heap::heap_hard_limit_oh[soh])
4868548707
{
4868648708
gc_heap::regions_range = gc_heap::heap_hard_limit;
@@ -48715,12 +48737,32 @@ HRESULT GCHeap::Initialize()
4871548737
{
4871648738
if (gc_heap::heap_hard_limit_oh[soh])
4871748739
{
48740+
// On 32bit we have next guarantees:
48741+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
48742+
// 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)
48743+
// 0 <= heap_hard_limit_oh[loh] <= 1Gb or < 2Gb
48744+
// 0 <= heap_hard_limit_oh[poh] <= 1Gb or < 2Gb
48745+
// 0 <= large_seg_size <= 1Gb or <= 2Gb (alignment and round up)
48746+
// 0 <= pin_seg_size <= 1Gb or <= 2Gb (alignment and round up)
48747+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 4Gb
48748+
// 4Gb overflow is ok, because 0 size allocation will fail
4871848749
large_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[loh], nhp), seg_size_from_config);
4871948750
pin_seg_size = max (gc_heap::adjust_segment_size_hard_limit (gc_heap::heap_hard_limit_oh[poh], nhp), seg_size_from_config);
4872048751
}
4872148752
else
4872248753
{
48754+
// On 32bit we have next guarantees:
48755+
// 0 <= heap_hard_limit <= 1Gb (from gc_heap::compute_hard_limit)
48756+
// 0 <= soh_segment_size <= 1Gb
48757+
// 0 <= large_seg_size <= 1Gb
48758+
// 0 <= pin_seg_size <= 1Gb
48759+
// 0 <= soh_segment_size + large_seg_size + pin_seg_size <= 3Gb
48760+
#ifdef HOST_64BIT
4872348761
large_seg_size = gc_heap::use_large_pages_p ? gc_heap::soh_segment_size : gc_heap::soh_segment_size * 2;
48762+
#else //HOST_64BIT
48763+
assert (!gc_heap::use_large_pages_p);
48764+
large_seg_size = gc_heap::soh_segment_size;
48765+
#endif //HOST_64BIT
4872448766
pin_seg_size = large_seg_size;
4872548767
}
4872648768
if (gc_heap::use_large_pages_p)
@@ -52984,16 +53026,45 @@ int GCHeap::RefreshMemoryLimit()
5298453026
return gc_heap::refresh_memory_limit();
5298553027
}
5298653028

53029+
bool gc_heap::compute_hard_limit_from_heap_limits()
53030+
{
53031+
#ifndef HOST_64BIT
53032+
// need to consider overflows:
53033+
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)
53034+
|| (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)
53035+
|| (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)))
53036+
{
53037+
return false;
53038+
}
53039+
#endif //!HOST_64BIT
53040+
53041+
heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
53042+
return true;
53043+
}
53044+
53045+
// On 32bit we have next guarantees for limits:
53046+
// 1) heap-specific limits:
53047+
// 0 <= (heap_hard_limit = heap_hard_limit_oh[soh] + heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh]) < 4Gb
53048+
// a) 0 <= heap_hard_limit_oh[soh] < 2Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
53049+
// b) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] < 2Gb, 0 <= heap_hard_limit_oh[poh] <= 1Gb
53050+
// c) 0 <= heap_hard_limit_oh[soh] <= 1Gb, 0 <= heap_hard_limit_oh[loh] <= 1Gb, 0 <= heap_hard_limit_oh[poh] < 2Gb
53051+
// 2) same limit for all heaps:
53052+
// 0 <= heap_hard_limit <= 1Gb
53053+
//
53054+
// These ranges guarantee that calculation of soh_segment_size, loh_segment_size and poh_segment_size with alignment and round up won't overflow,
53055+
// as well as calculation of sum of them (overflow to 0 is allowed, because allocation with 0 size will fail later).
5298753056
bool gc_heap::compute_hard_limit()
5298853057
{
5298953058
heap_hard_limit_oh[soh] = 0;
52990-
#ifdef HOST_64BIT
53059+
5299153060
heap_hard_limit = (size_t)GCConfig::GetGCHeapHardLimit();
5299253061
heap_hard_limit_oh[soh] = (size_t)GCConfig::GetGCHeapHardLimitSOH();
5299353062
heap_hard_limit_oh[loh] = (size_t)GCConfig::GetGCHeapHardLimitLOH();
5299453063
heap_hard_limit_oh[poh] = (size_t)GCConfig::GetGCHeapHardLimitPOH();
5299553064

53065+
#ifdef HOST_64BIT
5299653066
use_large_pages_p = GCConfig::GetGCLargePages();
53067+
#endif //HOST_64BIT
5299753068

5299853069
if (heap_hard_limit_oh[soh] || heap_hard_limit_oh[loh] || heap_hard_limit_oh[poh])
5299953070
{
@@ -53005,8 +53076,10 @@ bool gc_heap::compute_hard_limit()
5300553076
{
5300653077
return false;
5300753078
}
53008-
heap_hard_limit = heap_hard_limit_oh[soh] +
53009-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
53079+
if (!compute_hard_limit_from_heap_limits())
53080+
{
53081+
return false;
53082+
}
5301053083
}
5301153084
else
5301253085
{
@@ -53034,9 +53107,22 @@ bool gc_heap::compute_hard_limit()
5303453107
heap_hard_limit_oh[soh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_soh / (uint64_t)100);
5303553108
heap_hard_limit_oh[loh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_loh / (uint64_t)100);
5303653109
heap_hard_limit_oh[poh] = (size_t)(total_physical_mem * (uint64_t)percent_of_mem_poh / (uint64_t)100);
53037-
heap_hard_limit = heap_hard_limit_oh[soh] +
53038-
heap_hard_limit_oh[loh] + heap_hard_limit_oh[poh];
53110+
53111+
if (!compute_hard_limit_from_heap_limits())
53112+
{
53113+
return false;
53114+
}
5303953115
}
53116+
#ifndef HOST_64BIT
53117+
else
53118+
{
53119+
// need to consider overflows
53120+
if (heap_hard_limit > max_heap_hard_limit / 2)
53121+
{
53122+
return false;
53123+
}
53124+
}
53125+
#endif //!HOST_64BIT
5304053126
}
5304153127

5304253128
if (heap_hard_limit_oh[soh] && (!heap_hard_limit_oh[poh]) && (!use_large_pages_p))
@@ -53050,9 +53136,17 @@ bool gc_heap::compute_hard_limit()
5305053136
if ((percent_of_mem > 0) && (percent_of_mem < 100))
5305153137
{
5305253138
heap_hard_limit = (size_t)(total_physical_mem * (uint64_t)percent_of_mem / (uint64_t)100);
53139+
53140+
#ifndef HOST_64BIT
53141+
// need to consider overflows
53142+
if (heap_hard_limit > max_heap_hard_limit / 2)
53143+
{
53144+
return false;
53145+
}
53146+
#endif //!HOST_64BIT
5305353147
}
5305453148
}
53055-
#endif //HOST_64BIT
53149+
5305653150
return true;
5305753151
}
5305853152

@@ -53077,12 +53171,12 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5307753171
}
5307853172
}
5307953173
}
53174+
#endif //HOST_64BIT
5308053175

5308153176
if (heap_hard_limit && (heap_hard_limit < new_current_total_committed))
5308253177
{
5308353178
return false;
5308453179
}
53085-
#endif //HOST_64BIT
5308653180

5308753181
#ifdef USE_REGIONS
5308853182
{
@@ -53101,9 +53195,24 @@ bool gc_heap::compute_memory_settings(bool is_initialization, uint32_t& nhp, uin
5310153195
seg_size_from_config = (size_t)GCConfig::GetSegmentSize();
5310253196
if (seg_size_from_config)
5310353197
{
53104-
seg_size_from_config = adjust_segment_size_hard_limit_va (seg_size_from_config);
53198+
seg_size_from_config = use_large_pages_p ? align_on_segment_hard_limit (seg_size_from_config) :
53199+
#ifdef HOST_64BIT
53200+
round_up_power2 (seg_size_from_config);
53201+
#else //HOST_64BIT
53202+
round_down_power2 (seg_size_from_config);
53203+
seg_size_from_config = min (seg_size_from_config, max_heap_hard_limit / 2);
53204+
#endif //HOST_64BIT
5310553205
}
5310653206

53207+
// On 32bit we have next guarantees:
53208+
// 0 <= seg_size_from_config <= 1Gb (from max_heap_hard_limit/2)
53209+
// a) heap-specific limits:
53210+
// 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)
53211+
// 0 <= heap_hard_limit_oh[soh] <= 1Gb or < 2Gb
53212+
// 0 <= soh_segment_size <= 1Gb or <= 2Gb (alignment and round up)
53213+
// b) same limit for all heaps:
53214+
// 0 <= heap_hard_limit <= 1Gb
53215+
// 0 <= soh_segment_size <= 1Gb
5310753216
size_t limit_to_check = (heap_hard_limit_oh[soh] ? heap_hard_limit_oh[soh] : heap_hard_limit);
5310853217
soh_segment_size = max (adjust_segment_size_hard_limit (limit_to_check, nhp), seg_size_from_config);
5310953218
}

src/coreclr/gc/gcpriv.h

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

33843384
PER_HEAP_ISOLATED_METHOD BOOL dt_high_memory_load_p();
33853385

3386+
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit_from_heap_limits();
3387+
33863388
PER_HEAP_ISOLATED_METHOD bool compute_hard_limit();
33873389

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