From c3ba83ff93d7d0d4134e8b23b690cc2391d1ad85 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 27 Jan 2026 09:39:08 +0100 Subject: [PATCH] use less aggressive v8 GC Isolate.lowMemoryNotification runs an aggrissive GC. Using Isolate.memoryPressureNotification allow a more granular control of GC. --- src/browser/Browser.zig | 2 +- src/browser/Page.zig | 9 ++++----- src/browser/js/Env.zig | 18 ++++++++++++++++++ src/browser/js/Isolate.zig | 10 ++++++++++ 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/browser/Browser.zig b/src/browser/Browser.zig index 70b04429..1d514a12 100644 --- a/src/browser/Browser.zig +++ b/src/browser/Browser.zig @@ -100,7 +100,7 @@ pub fn closeSession(self: *Browser) void { session.deinit(); self.session = null; _ = self.session_arena.reset(.{ .retain_with_limit = 1 * 1024 * 1024 }); - self.env.lowMemoryNotification(); + self.env.memoryPressureNotification(.critical); } } diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 0cdd177f..1c8cd701 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -253,7 +253,7 @@ fn reset(self: *Page, comptime initializing: bool) !void { } if (comptime initializing == false) { - // Removins the context triggers the linked inspector. + // Removing the context triggers the linked inspector. // It seems to append a collect task to the message loop. self._session.executor.removeContext(); @@ -262,10 +262,9 @@ fn reset(self: *Page, comptime initializing: bool) !void { // will run after the GC and we will use memory after free. self._session.browser.runMessageLoop(); - // We force a garbage collection with lowMemoryNotification between - // page navigations to keep v8 memory usage as low as possible. - // Calling immediately after a runMessageLoop ensure - self._session.browser.env.lowMemoryNotification(); + // We force a garbage collection between page navigations to keep v8 + // memory usage as low as possible. + self._session.browser.env.memoryPressureNotification(.moderate); self._script_manager.shutdown = true; self._session.browser.http_client.abort(); diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 78fde66a..69f7cd20 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -24,6 +24,7 @@ const log = @import("../../log.zig"); const bridge = @import("bridge.zig"); const Context = @import("Context.zig"); +const Isolate = @import("Isolate.zig"); const Platform = @import("Platform.zig"); const Snapshot = @import("Snapshot.zig"); const Inspector = @import("Inspector.zig"); @@ -193,6 +194,8 @@ pub fn newExecutionWorld(self: *Env) !ExecutionWorld { // a Context, it's managed by the garbage collector. We use the // `lowMemoryNotification` call on the isolate to encourage v8 to free // any contexts which have been freed. +// This GC is very aggressive. Use memoryPressureNotification for less +// aggressive GC passes. pub fn lowMemoryNotification(self: *Env) void { var handle_scope: js.HandleScope = undefined; handle_scope.init(self.isolate); @@ -200,6 +203,21 @@ pub fn lowMemoryNotification(self: *Env) void { self.isolate.lowMemoryNotification(); } +// V8 doesn't immediately free memory associated with +// a Context, it's managed by the garbage collector. We use the +// `memoryPressureNotification` call on the isolate to encourage v8 to free +// any contexts which have been freed. +// The level indicates the aggressivity of the GC required: +// moderate speeds up incremental GC +// critical runs one full GC +// For a more aggressive GC, use lowMemoryNotification. +pub fn memoryPressureNotification(self: *Env, level: Isolate.MemoryPressureLevel) void { + var handle_scope: js.HandleScope = undefined; + handle_scope.init(self.isolate); + defer handle_scope.deinit(); + self.isolate.memoryPressureNotification(level); +} + pub fn dumpMemoryStats(self: *Env) void { const stats = self.isolate.getHeapStatistics(); std.debug.print( diff --git a/src/browser/js/Isolate.zig b/src/browser/js/Isolate.zig index fdede915..74974cc0 100644 --- a/src/browser/js/Isolate.zig +++ b/src/browser/js/Isolate.zig @@ -57,6 +57,16 @@ pub fn lowMemoryNotification(self: Isolate) void { v8.v8__Isolate__LowMemoryNotification(self.handle); } +pub const MemoryPressureLevel = enum(u32) { + none = v8.kNone, + moderate = v8.kModerate, + critical = v8.kCritical, +}; + +pub fn memoryPressureNotification(self: Isolate, level: MemoryPressureLevel) void { + v8.v8__Isolate__MemoryPressureNotification(self.handle, @intFromEnum(level)); +} + pub fn notifyContextDisposed(self: Isolate) void { _ = v8.v8__Isolate__ContextDisposedNotification(self.handle); }