Compare
Side-by-side on the decisions that recur: process vs thread, threads vs async, mutex vs semaphore, blocking vs non-blocking I/O, container vs VM — with when to choose each.
Process vs ThreadThreads vs Async / event loopConcurrency vs ParallelismMutex vs SemaphoreBlocking I/O vs Non-blocking / async I/Oselect / poll vs epoll / kqueueContainer vs Virtual machineStack vs HeapPipe vs Shared memoryOS page cache vs Application cache
| Stack | Heap | |
|---|---|---|
| Lifetime | Tied to the function call: pushed on entry, popped on return | Explicit (free/delete) or garbage-collected; outlives the call |
| Allocation | Move the stack pointer — a single instruction | Allocator search, possibly brk/mmap to the kernel; tens of ns to µs |
| Size | Fixed per thread: ~8 MB main thread on Linux, 1 MB on Windows, often 512 KB–2 MB for worker threads | Bounded by address space and RAM; grows on demand |
| Failure | Stack overflow → guard page → SIGSEGV (Linux) / STATUS_STACK_OVERFLOW (Windows) | Leaks, fragmentation, std::bad_alloc/NULL, OOM killer |
| Thread safety | Private to the thread by construction | Shared across threads — the allocator locks or uses per-thread arenas |
| In managed runtimes | JS and Python frames live on a runtime-managed stack; recursion limit ~10k (Node) / 1000 (CPython default) | Every object is heap-allocated and GC-managed; escape analysis may stack-allocate in JITs |
| Choose this when | Small, fixed-size, call-scoped data — locals, small arrays, iterators — for speed and automatic cleanup. | Anything large, dynamically sized, shared between threads, or that must outlive the function that created it. |