# electron-performance Reduce Electron's memory/CPU footprint: tiered Chromium/V8 command-line flags (memory-, GPU-, and Raspberry-Pi-generation-aware), a runtime GC/cache monitor with total-process memory tracking, and per-window memory helpers. CommonJS output, written in TypeScript (ships `.d.ts`). ## Install ```sh npm install electron-performance ``` `@oxmc/node-gpuinfo` is an optional native dependency used for GPU-tier detection; if it fails to install on a given platform/arch, GPU detection just degrades to an empty list (flags fall back to the "no GPU" branch) instead of breaking the install. ## Usage Three independent pieces. Wire in whichever you need. ### 1. Command-line flags — call before `app.whenReady()` ```js const { app } = require('electron'); const { applyCommandLineFlags } = require('electron-performance'); // Must run before app is ready — Chromium ignores switches set later. const system = applyCommandLineFlags(app); ``` Disable specific categories, or override the computed heap/cache sizes: ```js applyCommandLineFlags(app, { enable: { spellcheck: false, notifications: false }, // leave these Chromium defaults alone jsHeapSizeMB: 1024, // override the auto-tiered --max-old-space-size }); ``` `computeFlags(system, options)` is the pure version (no `app`, just returns the flag list) if you want to inspect/test the decision before applying it: ```js const { getSystemInfo, computeFlags } = require('electron-performance'); const system = getSystemInfo(); console.log(computeFlags(system)); ``` ### 2. Runtime memory monitor — call after `app.whenReady()` Periodic + pressure-triggered GC, periodic session-cache clearing, and a pressure-triggered **deep clean**: when total RSS across *every* process (main, renderers, GPU, utility — via `app.getAppMetrics()`, not just main's own heap) crosses a threshold, it wipes service worker/cache-storage data on every window's session and forces GC. That total-memory check matters — for a multi-window or webview-style app, almost all memory lives in renderer processes, which `process.memoryUsage()` can't see at all. ```js const { app, BrowserWindow } = require('electron'); const { startMemoryMonitor } = require('electron-performance'); app.whenReady().then(() => { const monitor = startMemoryMonitor({ app, BrowserWindow }, { system }); // reuse the SystemInfo from step 1 // monitor.stop() to tear down // monitor.forceGc() / monitor.deepClean() to run either immediately // monitor.getMemoryUsage() / monitor.getTotalMemoryUsage() to inspect on demand }); ``` Omit `BrowserWindow` (with `clearCache: false, deepClean: false`) if you only want the GC loop. ### 3. Per-window helpers ```js const { getRecommendedWebPreferences, attachWindowMemoryOptimizations } = require('electron-performance'); const win = new BrowserWindow({ webPreferences: getRecommendedWebPreferences({ preload: '...' }), }); attachWindowMemoryOptimizations(win, { onSettleHidden: () => monitor.forceGc(), // clears this window's cache ~15s after hide/minimize, cancels if shown again muteAudioOnHide: true, // opt-in: mute audio/video while hidden, unmute on show }); ``` ## Tuning tiers - **Memory tier**: `< 4096MB` low, `< 8192MB` medium, else high — drives `--max-old-space-size`, `--max-semi-space-size`, disk cache size, and renderer process limits. - **GPU tier**: discrete GPU present / integrated-only / none — drives GPU-acceleration flags (`@oxmc/node-gpuinfo`, filtered for software/virtual renderers). - **Raspberry Pi**: detected via `detect-rpi`; generations 1–3 get aggressive tuning (GPU disabled, tiny heap), gen 4 moderate, gen 5 light — this branch takes priority over the generic memory/GPU tiering above. All of the above can be disabled per-category via `options.enable` (see `FlagCategories` in `types.ts`), or bypassed entirely by passing your own `SystemInfo`. ## Notable design decisions - **`keepBackgroundActive` defaults to off** (opt-in). It's tempting to disable Chromium's renderer backgrounding/timer-throttling for snappier background windows, but that undoes one of the cheapest, biggest memory/CPU wins Chromium gives you for free: letting hidden/minimized windows' renderers actually idle. Only set `enable: { keepBackgroundActive: true }` if you need background windows to stay fully responsive (e.g. continuous audio/streaming). - **`enable-features`/`disable-features` merge instead of clobber.** Chromium's `commandLine.appendSwitch(name, value)` overwrites a switch if called more than once with the same name rather than merging comma-lists — call it from two different tuning branches and the second silently drops the first. `computeFlags` merges repeated `enable-features`/`disable-features` calls into one comma-joined switch internally so nothing gets lost. - **`global.gc` actually works in the main process.** `--js-flags=--expose-gc` set via `commandLine.appendSwitch` only reaches renderer/utility processes — Electron's main process already has V8 initialized by the time app code runs, so that switch alone never gives main a working `global.gc`. `startMemoryMonitor` self-enables it via `v8.setFlagsFromString('--expose-gc')` + `vm.runInNewContext('gc')` instead. Exposed standalone as `enableMainProcessGc()`, and toggleable via `{ exposeGc: false }`. - **Deep clean is conservative by default.** `deepCleanStorages` defaults to `['serviceworkers', 'cachestorage', 'shadercache']` — memory-heavy but not user session state — deliberately excluding `cookies`/`localstorage`/`indexdb` so a deep clean can't silently log a user out of a page they're viewing. Pass your own list for a more aggressive clean if that tradeoff is acceptable for your app. - **`disableBackForwardCache`** (default `'auto'`, low memory tier only) turns off BFCache, which otherwise keeps a fully-frozen copy of every recently-navigated-away-from page resident in memory for instant back/forward. - **`rendererProcessLimit`** caps `--renderer-process-limit` by memory tier — matters most for apps that open many BrowserWindows/WebContentsViews, each normally its own process.