fix growing gc

This commit is contained in:
2026-02-13 04:33:32 -06:00
parent cb9d6e0c0e
commit 9f0fd84f4f

View File

@@ -724,10 +724,27 @@ void *js_malloc (JSContext *ctx, size_t size) {
JS_ThrowOutOfMemory (ctx);
return NULL;
}
/* Re-check after GC */
/* Re-check after GC — if still no room, grow and retry.
The second GC is cheap: data was just compacted so there is
almost no garbage to skip. */
if ((uint8_t *)ctx->heap_free + size > (uint8_t *)ctx->heap_end) {
JS_ThrowOutOfMemory (ctx);
return NULL;
size_t need = (size_t)((uint8_t *)ctx->heap_free - (uint8_t *)ctx->heap_base) + size;
size_t ns = ctx->current_block_size;
while (ns < need && ns < (1ULL << BUDDY_MAX_ORDER))
ns *= 2;
ctx->next_block_size = ns;
if (ctx_gc (ctx, 1, size) < 0) {
JS_ThrowOutOfMemory (ctx);
return NULL;
}
/* The retry pass relocates compacted data — 0% recovery is expected.
Reset next_block_size so the poor-recovery heuristic inside ctx_gc
doesn't cascade into further unnecessary doubling. */
ctx->next_block_size = ctx->current_block_size;
if ((uint8_t *)ctx->heap_free + size > (uint8_t *)ctx->heap_end) {
JS_ThrowOutOfMemory (ctx);
return NULL;
}
}
}
#endif
@@ -1167,15 +1184,17 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
printf("ctx_gc: from_base=%p from_end=%p size=%zu\n", (void*)from_base, (void*)from_end, old_heap_size);
#endif
/* Request new block from runtime.
When allow_grow is set and the pending allocation won't fit in the
current next_block_size, jump straight to a block that can hold
live_data + alloc_size instead of doubling one step at a time. */
size_t new_size = ctx->next_block_size;
/* Size the new block. Start at current_block_size (guaranteed >= used
portion, so all live data fits). Only grow when:
- next_block_size was bumped by the poor-recovery heuristic, or
- alloc_size alone exceeds the block (rare large allocation).
Crucially, do NOT add live_est to the sizing — it counts garbage
and causes exponential heap growth even with excellent recovery. */
size_t new_size = ctx->current_block_size;
if (allow_grow) {
size_t live_est = (size_t)(from_end - from_base); /* upper bound on live data */
size_t need = live_est + alloc_size;
while (new_size < need && new_size < (1ULL << BUDDY_MAX_ORDER))
if (ctx->next_block_size > new_size)
new_size = ctx->next_block_size;
while (new_size < alloc_size && new_size < (1ULL << BUDDY_MAX_ORDER))
new_size *= 2;
}
#ifdef POISON_HEAP
@@ -1324,12 +1343,15 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
}
#ifdef DUMP_GC
printf ("\nGC: %zu -> %zu bytes, recovered %zu (%.1f%%)%s\n",
printf ("\nGC: %zu -> %zu bytes (used %zu -> %zu), recovered %zu (%.1f%%)%s\n",
old_heap_size,
new_size,
old_used,
new_used,
recovered,
old_used > 0 ? (recovered * 100.0 / old_used) : 0.0,
will_grow ? ", heap will grow" : "");
fflush(stdout);
#endif
return 0;