diff --git a/eleventy.config.js b/eleventy.config.js index 6e3fa9a..b8431ae 100644 --- a/eleventy.config.js +++ b/eleventy.config.js @@ -1124,6 +1124,7 @@ export default function (eleventyConfig) { try { execFileSync(process.execPath, [ "--max-old-space-size=768", + "--expose-gc", resolve(__dirname, "lib", "og-cli.js"), contentDir, cacheDir, diff --git a/lib/og.js b/lib/og.js index b13df90..4feb24b 100644 --- a/lib/og.js +++ b/lib/og.js @@ -298,6 +298,8 @@ export async function generateOgImages(contentDir, cacheDir, siteName) { let skipped = 0; const newManifest = {}; const SAVE_INTERVAL = 10; + const GC_INTERVAL = 50; + const hasGC = typeof global.gc === "function"; for (const filePath of mdFiles) { const raw = readFileSync(filePath, "utf8"); @@ -335,10 +337,21 @@ export async function generateOgImages(contentDir, cacheDir, siteName) { if (generated % SAVE_INTERVAL === 0) { writeFileSync(manifestPath, JSON.stringify(newManifest, null, 2)); } + + // Force GC to reclaim Satori/Resvg WASM native memory. + // V8 doesn't track native heap (Satori Yoga WASM + Resvg Rust WASM), + // so without periodic GC the JS wrappers accumulate and native memory + // grows unbounded. With --expose-gc this keeps peak RSS under control. + if (hasGC && generated % GC_INTERVAL === 0) { + global.gc(); + } } + if (hasGC) global.gc(); writeFileSync(manifestPath, JSON.stringify(newManifest, null, 2)); + const mem = process.memoryUsage(); console.log( - `[og] Generated ${generated} images, skipped ${skipped} (cached or have photos)`, + `[og] Generated ${generated} images, skipped ${skipped} (cached or have photos)` + + ` | RSS: ${(mem.rss / 1024 / 1024).toFixed(0)} MB, heap: ${(mem.heapUsed / 1024 / 1024).toFixed(0)} MB`, ); }