From 2ff8c23edfbc7934bf7f94cc4972258f460a5169 Mon Sep 17 00:00:00 2001 From: "Enzo Persillet (Tutez)" Date: Wed, 6 May 2026 21:03:57 +0200 Subject: [PATCH] Fix Immix working memory overflow with big blocks Use GetWorkingMemory() instead of recomputing the Immix block size through an int expression. With HXCPP_GC_BIG_BLOCKS, shifting the block count through an int overflows once regular heap usage reaches roughly 2 GiB, which can cause premature memory exhaustion or extremely long GC stalls. Also store GC memory tuning thresholds as size_t and parse their environment variables with strtoull so large values are not truncated. --- include/hx/GC.h | 4 ++-- src/hx/gc/GcCommon.cpp | 12 ++++++------ src/hx/gc/Immix.cpp | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/hx/GC.h b/include/hx/GC.h index 39480c2a6..a69523c97 100644 --- a/include/hx/GC.h +++ b/include/hx/GC.h @@ -158,10 +158,10 @@ extern int gPauseForCollect; // Minimum total memory - used + buffer for new objects -extern int sgMinimumWorkingMemory; +extern size_t sgMinimumWorkingMemory; // Minimum free memory - not counting used memory -extern int sgMinimumFreeSpace; +extern size_t sgMinimumFreeSpace; // Also ensure that the free memory is larger than this amount of used memory extern int sgTargetFreeSpacePercentage; diff --git a/src/hx/gc/GcCommon.cpp b/src/hx/gc/GcCommon.cpp index f336fd615..fdc93341a 100644 --- a/src/hx/gc/GcCommon.cpp +++ b/src/hx/gc/GcCommon.cpp @@ -27,11 +27,11 @@ extern void __hxt_new_string(void* result, int size); namespace hx { #if defined(HX_MACOS) || defined(HX_WINDOWS) || defined(HX_LINUX) || defined(__ORBIS__) -int sgMinimumWorkingMemory = 20*1024*1024; -int sgMinimumFreeSpace = 10*1024*1024; +size_t sgMinimumWorkingMemory = 20*1024*1024; +size_t sgMinimumFreeSpace = 10*1024*1024; #else -int sgMinimumWorkingMemory = 8*1024*1024; -int sgMinimumFreeSpace = 4*1024*1024; +size_t sgMinimumWorkingMemory = 8*1024*1024; +size_t sgMinimumFreeSpace = 4*1024*1024; #endif // Once you use more than the minimum, this kicks in... int sgTargetFreeSpacePercentage = 100; @@ -48,7 +48,7 @@ void CommonInitAlloc() const char *minimumWorking = getenv("HXCPP_MINIMUM_WORKING_MEMORY"); if (minimumWorking) { - int mem = atoi(minimumWorking); + size_t mem = strtoull(minimumWorking, 0, 10); if (mem>0) sgMinimumWorkingMemory = mem; } @@ -56,7 +56,7 @@ void CommonInitAlloc() const char *minimumFreeSpace = getenv("HXCPP_MINIMUM_FREE_SPACE"); if (minimumFreeSpace) { - int mem = atoi(minimumFreeSpace); + size_t mem = strtoull(minimumFreeSpace, 0, 10); if (mem>0) sgMinimumFreeSpace = mem; } diff --git a/src/hx/gc/Immix.cpp b/src/hx/gc/Immix.cpp index 74672a54a..a434adc21 100644 --- a/src/hx/gc/Immix.cpp +++ b/src/hx/gc/Immix.cpp @@ -3543,11 +3543,11 @@ class GlobalAllocator #if defined(SHOW_MEM_EVENTS_VERBOSE) || defined(SHOW_FRAGMENTATION_BLOCKS) if (inJustBorrowing) { - GCLOG("Borrow Blocks %d = %d k\n", mAllBlocks.size(), (mAllBlocks.size() << IMMIX_BLOCK_BITS)>>10); + GCLOG("Borrow Blocks %d = %d k\n", mAllBlocks.size(), (int)(GetWorkingMemory() >> 10)); } else { - GCLOG("Blocks %d = %d k\n", mAllBlocks.size(), (mAllBlocks.size() << IMMIX_BLOCK_BITS)>>10); + GCLOG("Blocks %d = %d k\n", mAllBlocks.size(), (int)(GetWorkingMemory() >> 10)); } #endif @@ -5235,7 +5235,7 @@ class GlobalAllocator #endif // Large alloc target - int blockSize = mAllBlocks.size()< mLargeAllocSpace) mLargeAllocSpace = blockSize; mLargeAllocForceRefresh = mLargeAllocated + mLargeAllocSpace;