fix growing gc
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user