# Streambert Memory Management and V8 Heap Limits: Optimizing Electron Renderer Memory

> Optimize Electron renderer memory with Streambert. Learn how Streambert manages v8 heap limits and uses manual garbage collection to prevent crashes on low-end hardware.

- Repository: [true_lock/streambert](https://github.com/truelockmc/streambert)
- Tags: performance
- Published: 2026-05-21

---

**Streambert caps its Electron renderer V8 heap at 256 MiB using `--max-old-space-size=256` and exposes `global.gc()` to manually trigger garbage collection after video playback sessions, preventing out-of-memory crashes on low-end hardware.**

Streambert is an Electron-based streaming application designed to run smoothly on resource-constrained devices. By analyzing the `truelockmc/streambert` source code, we can see exactly how **Streambert memory management v8 heap limits** are implemented through explicit V8 flags, dual-process garbage collection triggers, and aggressive native cache eviction strategies.

## Configuring V8 Heap Limits in the Main Process

Streambert’s main process ([`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js)) explicitly configures the renderer process V8 engine during `BrowserWindow` creation. This proactive configuration prevents the JavaScript heap from growing unbounded during extended streaming sessions.

### Setting the Old Generation Cap

In [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js) at lines 28–29, the `webPreferences` object passes V8 flags through the `additionalArguments` array:

```javascript
webPreferences: {
  preload: path.join(__dirname, "preload.js"),
  contextIsolation: true,
  nodeIntegration: false,
  webviewTag: true,
  backgroundThrottling: true,
  spellcheck: false,
  // Caps the renderer's V8 heap + exposes gc() for manual GC hints after navigation
  additionalArguments: [
    "--js-flags=--max-old-space-size=256 --expose-gc",
  ],
},

```

The `--max-old-space-size=256` flag restricts V8’s **old generation** heap to 256 MiB. This limit forces the garbage collector to run more frequently rather than allowing memory to accumulate indefinitely, which is critical for preventing renderer process crashes during heavy video playback.

### Exposing the Garbage Collector

The same `additionalArguments` entry includes `--expose-gc`, which injects a `global.gc()` function into the JavaScript context. According to the source code at [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js), this exposed function is used to force synchronous garbage collection at strategic moments when the application knows large objects (like video buffers) have become unreachable.

## Manual Memory Reclamation Workflows

Beyond automatic V8 garbage collection, Streambert implements explicit memory reclamation workflows that target both JavaScript-managed memory and native resources that V8 cannot see.

### Post-Navigation Cleanup in the Main Process

When a player WebView stops, the main process triggers garbage collection immediately. At [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js) line 357, the code checks for the exposed function and invokes it:

```javascript
if (typeof global.gc === "function") global.gc();

```

This call runs a full collection on the main process’s heap, reclaiming memory from the closed player components before the UI renders the next screen.

### Renderer-Side GC Invocation

To ensure the renderer process also releases memory, line 361 sends an explicit GC request into the renderer via `executeJavaScript`:

```javascript
mw.webContents.executeJavaScript("if(typeof gc==='function') gc();");

```

This **dual-process collection strategy** ensures that both the main Electron process and the renderer WebContents free unused objects, minimizing the application's total memory footprint.

### Player Session Cache Flushing

Native-level memory (HTTP caches, shader compilations) exists outside the V8 heap and requires explicit clearing. In [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js) lines 49‑55, Streambert configures a persistent `persist:player` session that is aggressively cleared when playback ends:

```javascript
// Clear cache and storage for the player session to free native memory
ps.clearCache().then(() => {
  ps.clearStorageData({
    storages: ["shadercache", "cachestorage"],
    quotas: ["temporary", "persistent"],
  });
});

```

The `clearCache()` and `clearStorageData()` calls free GPU shader caches and HTTP response caches that would otherwise persist across video sessions.

## In-Memory Cache Management

Streambert also bounds application-level caches that could grow indefinitely during long usage sessions.

### TMDB API Response Caching

The TMDB utility ([`src/utils/api.js`](https://github.com/truelockmc/streambert/blob/main/src/utils/api.js)) maintains an in-memory response cache to reduce API calls. At line 107, the code implements **TTL-based eviction** to prevent unbounded growth:

```javascript
// Evict stale entries to prevent unbounded memory growth
if (Date.now() - entry.timestamp > MAX_AGE_MS) {
  delete cache[key];
}

```

Entries older than the configurable maximum age (default 10 minutes) are deleted, ensuring the cache remains small even during extended browsing.

### Block Statistics Tracking

For runtime analytics, [`src/ipc/blockStats.js`](https://github.com/truelockmc/streambert/blob/main/src/ipc/blockStats.js) line 56 maintains a lightweight in-memory counter:

```javascript
// Update alltime in-memory

```

This counter accumulates statistics only for the current application lifecycle and is automatically cleared on restart, avoiding permanent memory bloat in the V8 heap.

## Complete Player Memory Cleanup Flow

The comprehensive cleanup sequence is implemented in [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js) lines 329‑364 within the "Player memory cleanup" block. When a video stops, Streambert executes a three-step reclamation process:

1. **Destroy Tracked WebContents:** Iterates through `playerWcIds` to mute and destroy each player WebView instance.
2. **Flush Native Caches:** Calls `clearCache()` and `clearStorageData()` on the persistent player session.
3. **Trigger Dual GC:** Runs `global.gc()` in the main process, then injects `gc()` execution into the renderer.

This coordinated approach fully reclaims memory allocated by the player WebView, including both V8-managed JavaScript objects and Electron’s native C++ resources.

## Practical Implementation Examples

You can interact with or modify Streambert’s memory management in several ways.

### Adjusting the Heap Limit at Build Time

For machines with more available RAM, increase the old-space size in [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js):

```javascript
additionalArguments: [
  "--js-flags=--max-old-space-size=512 --expose-gc", // 512 MiB instead of 256
],

```

### Manually Triggering GC from the Renderer

Any renderer process code can force collection:

```javascript
if (typeof gc === "function") {
  gc(); // forces V8 to run a collection
}

```

### Clearing the TMDB Cache On Demand

Import the utility to purge cached API responses immediately:

```javascript
import { clearTmdbCache } from "./utils/api";

function onUserRequestRefresh() {
  clearTmdbCache(); // empties the cache immediately
}

```

### Monitoring Heap Usage (Debug Builds)

Inspect current memory consumption from the main process:

```javascript
const used = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(`Heap used: ${used.toFixed(2)} MiB`);

```

## Summary

- **V8 heap limits** are enforced via `--max-old-space-size=256` in [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js) to cap old-generation memory at 256 MiB.
- **Manual garbage collection** is enabled by `--expose-gc` and triggered in both main and renderer processes after video playback ends.
- **Native cache eviction** clears HTTP and shader caches via `clearCache()` and `clearStorageData()` in the player session.
- **Application caches** use TTL-based eviction ([`src/utils/api.js`](https://github.com/truelockmc/streambert/blob/main/src/utils/api.js) line 107) and lifecycle-bound counters ([`src/ipc/blockStats.js`](https://github.com/truelockmc/streambert/blob/main/src/ipc/blockStats.js) line 56) to prevent unbounded growth.
- The complete cleanup workflow at [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js) lines 329‑364 coordinates WebContents destruction, native cache flushing, and dual-process GC.

## Frequently Asked Questions

### Why does Streambert limit the V8 heap to 256 MiB?

The 256 MiB cap in [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js) prevents the Electron renderer process from consuming all available system memory during video streaming, which keeps the UI responsive on low-end machines with limited RAM. This conservative limit forces more frequent garbage collection cycles rather than allowing memory to accumulate indefinitely.

### How does Streambert clean up memory after a video stops?

When playback ends, the code at [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js) lines 329‑364 executes a three-part cleanup: it destroys the player WebContents instances, clears the persistent session’s HTTP and shader caches via `clearStorageData()`, and triggers `global.gc()` in both the main process and renderer process to immediately reclaim unreachable objects.

### What happens to the TMDB API cache when it grows too large?

The TMDB helper in [`src/utils/api.js`](https://github.com/truelockmc/streambert/blob/main/src/utils/api.js) line 107 implements time-to-live (TTL) eviction. When retrieving cached entries, the code checks if `Date.now() - entry.timestamp` exceeds `MAX_AGE_MS` (default 10 minutes) and deletes stale entries, ensuring the in-memory cache never grows beyond a bounded set of recent responses.

### Can I increase the V8 heap limit if my machine has more RAM?

Yes. Edit the `additionalArguments` array in [`index.js`](https://github.com/truelockmc/streambert/blob/main/index.js) at lines 28‑29 and modify the `--max-old-space-size` value (for example, to `512` for 512 MiB). This adjustment requires rebuilding the application, as V8 flags are set at process startup and cannot be changed at runtime.