Copy-on-Write

graph TD f["fork()"] --> shared["Parent + Child both point<br/>to SAME physical pages (read-only)"] shared --> write["Child writes to a page"] write --> copy["Minor fault: kernel copies<br/>ONLY that page"] copy --> private["Child now has a private<br/>writable copy of that page"] shared -.->|"unwritten pages stay shared forever"| shared

Mechanics: fork() marks all shared parent/child pages read-only. First write to a shared page → minor fault → kernel copies that page → both processes get private writable copies. Only written pages are copied. fork() cost = O(page table size), not O(data size).

RDB snapshots: Redis calls fork(). Child inherits the full dataset view at zero copy cost. Parent continues writes, triggering CoW on modified pages. Child serializes snapshot to disk. Peak memory = dataset + pages modified during snapshot. For write-heavy workloads, this can temporarily double memory.

Go GC: doesn't use fork(). Concurrent tricolor mark-and-sweep with write barriers runs alongside the application.

Common pitfall

Assuming a fork()-based snapshot (like Redis's RDB) is "free" because fork() itself is cheap ignores what happens after — a write-heavy parent process triggers CoW on every modified page during the snapshot window, and for a workload with a high write rate touching a large fraction of memory, peak memory usage can approach double the dataset size for the snapshot's duration, exactly as the RDB example above notes. Provisioning RAM based only on steady-state usage, without accounting for this transient doubling during snapshots, is how a snapshot operation itself triggers an OOM kill.