From 2554c1083b99e9841ef7c7f72e8d7436b33baad3 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 19 May 2026 00:07:07 +0200 Subject: [PATCH] Fix heap_segment_used watermark after compaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After compact_phase, heap_segment_used can be stale — lower than the actual end of live data - because `plan_phase` sets `plan_allocated` beyond used for regions that receive relocated objects. When `decommit_region` later clears memory only up to used instead of committed (the large-pages / never_decommit_p path), the gap between used and plan_allocated retains dirty data from a previous region lifetime, causing heap corruption on the next GC cycle. Fix: At the end of `compact_phase`, bump heap_segment_used to `max(used, plan_allocated)` for every non-read-only region in the condemned generations and one generation above (the maximum compaction target range). The fix cost is zero when no compaction occurs. When compaction does occur, it avoids unnecessary `memclr` in `decommit_region` by keeping the used watermark accurate, so only truly unused memory is cleared. --- src/coreclr/gc/gc.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 13a3915f7b68cd..0cc95bca4d3a77 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -12638,6 +12638,11 @@ size_t gc_heap::decommit_heap_segment_pages_worker (heap_segment* seg, void gc_heap::decommit_heap_segment (heap_segment* seg) { #ifdef USE_REGIONS + if (use_large_pages_p) + { + return; + } + if (!dt_high_memory_load_p()) { return; @@ -38001,6 +38006,21 @@ void gc_heap::compact_phase (int condemned_gen_number, recover_saved_pinned_info(); +#ifdef USE_REGIONS + for (int i = 0; i <= min (condemned_gen_number + 1, (int)max_generation); i++) + { + generation* gen = generation_of (i); + for (heap_segment* region = generation_start_segment_rw (gen); + region != nullptr; + region = heap_segment_next_rw (region)) + { + uint8_t* plan_allocated = heap_segment_plan_allocated (region); + if (plan_allocated > heap_segment_used (region)) + heap_segment_used (region) = plan_allocated; + } + } +#endif //USE_REGIONS + concurrent_print_time_delta ("compact end"); dprintf (2, (ThreadStressLog::gcEndCompactMsg(), heap_number));