How Orca Handles Memory Management: Architecture, Collection, and Attribution
Orca uses a dedicated collector in the Electron main process that snapshots OS-level process data, attributes resource usage to work-trees via PTY registration, and exposes aggregated metrics through a type-safe IPC layer.
Orca is an Electron-based terminal workspace that tracks per-project resource consumption in real-time. Understanding how Orca memory management works requires examining its single-purpose collector architecture, which bridges raw operating system metrics with the renderer's React UI.
The Collector Architecture
The memory management system lives entirely in the main process within src/main/memory/collector.ts. It operates on a polling model that aggregates data from three sources: operating system processes, Electron's internal metrics API, and a local registry of pseudo-terminal (PTY) sessions.
PTY Registration (pty-registry.ts)
Before the collector can attribute memory to specific projects, every locally-spawned terminal must register itself. In src/main/memory/pty-registry.ts, PTYs store their work-tree ID, session ID, pane key, and PID in a side-table.
This registry enables downstream attribution. When the collector runs, it maps system PIDs back to specific work-trees using this registration data.
// Register a PTY when spawning a terminal (main process)
import { registerPty } from './memory/pty-registry';
registerPty({
ptyId: terminalId,
worktreeId: currentWorktreeId,
sessionId: sessionId,
paneKey: paneKey,
pid: child.pid, // may be null for a brief moment
});
Process Enumeration (enumerateUnix/enumerateWindows)
On each snapshot cycle, the collector executes platform-specific commands to build a ProcIndex map. enumerateUnix() parses ps output on macOS and Linux, while enumerateWindows() queries wmic on Windows.
Both functions return a flat list of all OS processes including PID, PPID, CPU percentage, and RSS (Resident Set Size). These values populate the ProcIndex used for subsequent tree walking.
Electron Process Bucketing (bucketElectronMetrics)
The collector retrieves Electron-specific metrics via app.getAppMetrics(). The bucketElectronMetrics() function categorizes these into main, renderer, and other buckets, converting working-set sizes from kilobytes to bytes for consistency with OS-level data.
Work-Tree Attribution and Aggregation
Raw process data remains useless until mapped to user projects. Orca solves this by walking process trees and aggregating resource consumption into WorktreeBucket objects.
Tree Walking and Deduplication (collectSubtree)
The collectSubtree function in src/main/memory/collector.ts recursively walks the process tree for each registered PTY. It sums CPU and memory for every descendant PID while tracking a claimed set to prevent double-counting shared child processes.
// Excerpt from collectMemorySnapshot in collector.ts
export async function collectMemorySnapshot(store: Store): Promise<MemorySnapshot> {
const procIndex = await enumerateProcesses(); // ps / wmic
const appBuckets = bucketElectronMetrics(); // Electron metrics
const ptys = listRegisteredPtys(); // PTY side-table
// Walk each PTY's subtree, sum CPU / memory, avoid double-counting
for (const pty of ptys) { … }
// Assemble final snapshot
return {
app: { … },
worktrees,
host: hostMetrics(),
collectedAt: Date.now(),
};
}
Ring Buffer History (HISTORY_CAPACITY)
To support UI sparklines without unbounded growth, the collector maintains time-series data in ring buffers. HISTORY_CAPACITY is set to 60 samples per key, with stale entries purged after HISTORY_STALE_MS (10 minutes) via sweepStaleHistory.
The pushHistorySample function updates these buffers during each snapshot, while readHistory retrieves the current window for the renderer.
IPC and Renderer Integration
The collector exposes data through a strict IPC boundary, preventing the renderer from directly accessing Node APIs.
Main Process IPC Handler (memory:getSnapshot)
In src/main/ipc/memory.ts, the memory:getSnapshot channel handler invokes collectMemorySnapshot(store) and returns the serialized MemorySnapshot type defined in src/shared/types.ts.
Preload Bridge (window.api.memory.getSnapshot)
The preload script at src/preload/index.ts (around line 2786) wraps the IPC handler, exposing window.api.memory.getSnapshot() to the isolated renderer context. This maintains Electron's security model while providing type-safe access.
// Request a memory snapshot from the renderer (e.g. in a React hook)
await window.api.memory.getSnapshot(); // returns a MemorySnapshot
Zustand State Slice (createMemorySlice)
The renderer caches snapshots in a Zustand slice located at src/renderer/src/store/slices/memory.ts. The createMemorySlice function deduplicates concurrent requests, manages loading states, and surfaces errors to the UI.
// The slice that performs the request (simplified)
const fetchMemorySnapshot = async () => {
const snap = await window.api.memory.getSnapshot(); // IPC call
set({ memorySnapshot: snap, memorySnapshotError: null });
};
Host-Level Metrics
Beyond per-work-tree attribution, the collector records machine-wide statistics via the hostMetrics() function. This uses Node's built-in os module to capture total system memory and CPU load, providing context for the relative percentages displayed in Orca's status bar.
UI components in src/renderer/src/components/status-bar/mergeSnapshotAndSessions.ts merge this snapshot data with session information to render the final resource indicators.
Summary
- Orca's memory management relies on a main-process collector that snapshots OS processes via
psorwmicand Electron'sapp.getAppMetrics(). - PTY registration in
pty-registry.tsenables attribution of arbitrary child processes to specific work-trees through PID tracking. - Tree-walking logic in
collectSubtreeaggregates resource usage while preventing double-counting through aclaimedset. - Ring buffers with automatic expiration (60 samples, 10-minute staleness) provide trend data without memory leaks.
- IPC isolation via
memory:getSnapshotand the preload bridge ensures security while exposing data throughwindow.api.memory.getSnapshot(). - Zustand slices handle caching and error states, feeding React components that display per-project memory usage in real-time.
Frequently Asked Questions
How does Orca prevent memory leaks in its history tracking?
Orca uses constant-size ring buffers (HISTORY_CAPACITY = 60) and automatic stale entry removal (HISTORY_STALE_MS = 10 minutes). The sweepStaleHistory function runs during each collection cycle, ensuring history data never grows unbounded regardless of uptime.
Can Orca track memory for processes spawned outside of its terminals?
No. Attribution requires PTY registration via registerPty() in src/main/memory/pty-registry.ts. Only processes descending from registered PTYs (and their children) appear in work-tree buckets. Host-level metrics capture system-wide totals, but per-project attribution depends on terminal registration.
Why does the collector run in the Electron main process instead of the renderer?
The collector requires Node.js APIs (child_process for ps/wmic, os for host metrics) and direct access to app.getAppMetrics(). Running in the main process maintains Electron's security boundaries while the preload script exposes only the specific getSnapshot() method to the renderer via IPC channels.
What happens if the snapshot request fails?
The Zustand slice in src/renderer/src/store/slices/memory.ts catches IPC failures and stores the error in memorySnapshotError. The UI can then display fallback states or retry logic without crashing the application, with the last successful snapshot remaining available for reference.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →