Memory & Resource Management
Who is allowed to hold how much memory, for how long, and what happens to the work when the answer is less than it asked for?
Who this mechanism is for: a developer who writes code, calls APIs and knows basic SQL, and who has seen an "out of memory" error, but has never studied how a database engine, a garbage collector or an operating system actually decides who gets memory. You do not need to know what a buffer pool, an arena or a memory grant is. Every technical term is explained the first time it appears, and again in the glossary at the end.
The story we will follow
Amara Okafor is a backend engineer at Voltara, a regional electricity utility. Voltara reads about one billion smart-meter readings per day (who used how much power, and when). Amara works on Meterbase, the in-house query engine that stores and analyses these readings. Meterbase runs on the Java virtual machine (JVM), has its own storage layer with a cache of disk pages, and runs on servers with 2 CPU sockets, 256 GB of RAM and local NVMe SSDs. Three kinds of work share these servers: a nightly billing batch, an analyst dashboard that sends thousands of short lookups, and ad-hoc big joins written by Yuki Tanaka, a data scientist. Dmitri Volkov is the site-reliability engineer (SRE) who is on call when something breaks at 3 am.
During one quarter Amara will meet five problems, one at the start of each article below. Each problem looks like a bug: a job killed for no reason, a program that freezes, a cache that suddenly stops working, a query that is six times slower after "only a little" extra work. Each one is actually a consequence of one fact about memory. At the end of each article we go back to Amara and solve her problem with the mechanisms of that part.
This is the main idea of this whole mechanism:
How this mechanism is organized
- 1
Memory is a space resource
Why is memory different from CPU, disk and network, and why does a database not simply trust the operating system with it?
CaseOOM-killed at 3:10, or seven hours with swap
- 2
Ownership
Who allocates, who frees, and where do the bytes physically sit? (arenas, off-heap memory, garbage-collector tuning, columnar buffer pools, string dictionaries, NUMA placement)
Case40-second freezes, and a process that looks like it leaks
- 3
Residency
When the data is larger than memory, which part is in memory right now? (page replacement: LRU, Clock, LRU-K, 2Q, ARC; pinning; page size)
CaseOne scan, and the hit rate drops to zero
- 4
Rationing
When many queries share the machine, who gets how much, and when is that decided? (memory grants, estimates, admission control, pressure signals, bandwidth isolation)
CaseA tiny lookup killed, a giant join spared
- 5
Degradation
What happens when the ration runs out? (spilling to disk, hash and sort spill, skew, operators that cannot spill, the kill decision)
Case5% spilled, 6 times slower
- 6
What memory management forbids
What can no mechanism escape, and what does each escape cost?
After that: a summary, hands-on labs, interview questions and a glossary.
A request: do not skip the arithmetic. Many results here are short formulas (for example usable frames = total − pinned − dirty). The shapes of these formulas, steep walls and sudden cliffs, are the real content.
What do I do, and what does each option cost?
Choose who owns the memory
An engine allocates memory for data it processes: leave it to the garbage collector, or take it off-heap as an arena, a typed pool, a dictionary or capped buffers? Four questions, then two checks on placement and peak.
ChooseTune a buffer pool
A database caches disk pages in a buffer pool: isolate scans, use Clock, or pay for 2Q or ARC? Three questions pick the replacement policy; three checks find the pool's usable size and the failures upstream of the policy.
ChooseRation memory across queries
Many queries share one machine's memory: list what cannot spill, check where grants come from, size grants in steps, decide between reservation and over-commit, and keep the pressure loop from oscillating.
DesignPlan for running out of memory
When a query outgrows its memory, it spills to disk or is killed: list what cannot spill, compute what spilling really costs on your hardware, guard against one oversized key, and make sure a killed query retries with more memory.
Design
Can I see it myself?
Arena versus `malloc`: the 50× claim
100 million 24-byte objects with malloc/free and with a bump arena; then fragmentation: resident memory against live bytes.
MeasureC compiler, PythonGC cost against headroom: `1 / (H/L − 1)`
A Java program with a live set of small objects at heap sizes from 1.25 to 7 times the live set: GC share of time against 1/(H/L − 1); then the live set off-heap.
MeasureJava 17+, PythonFind your NUMA effect (first touch) and measure it honestly
Scan a 1 GB array with all cores after single-threaded, parallel and interleaved first touch, in a Linux container: a single-node control run.
Real systemDockerLRU, random and Clock on a cyclic loop
LRU, random and Clock replaying a loop over 100 pages for pools of 1 to 100 frames; then a one-time scan against a hot set under LRU, 2Q and a scan ring.
SimulatePythonScan resistance in a real database (PostgreSQL)
PostgreSQL with a 128 MB buffer pool: a hot table's pages after a sequential scan of a 1 GB table, and after reading the same table through an index.
Real systemDocker, PythonGrant quantization: count the passes
The external sort pass count ⌈log_(M/B − 1)(N/M)⌉ for 1 TB across memory grants; then PostgreSQL sorting 5 million rows from work_mem 64 kB to 1 GB.
Real systemDocker, PythonThe spill cliff: 5% spilled, how much slower?
Measure memory and disk bandwidth, then spill 0%, 5% and 20% of an 800 MB array to a file and back: the slowdown against (1 − f) + 2·f·r.
MeasurePython with NumPySort versus hash under a hot key
50 million rows with 30% on one key under a 500 MB DuckDB limit: SUM/COUNT against median by key, and a grace partitioner's largest partition under 5 hash seeds.
MeasurePython with DuckDBQueueing on the spill device
Rate-limited sequential writers sharing one SSD at 20% to 100% of its measured throughput: completion latency against S/(1 − ρ).
MeasurePython
Can I reason about it under questioning?
Interview questions
Short questions on how data systems manage memory, such as ownership and allocation, buffer pools and replacement policies, memory grants and spilling, each with an answer you could give aloud and a link to the full explanation.
Scenarios
Longer problems on memory management in data systems, of the kind asked in senior interviews: each one estimated with numbers, diagnosed, and answered with its trade-offs.
The tensions this mechanism takes a position on. Each recurs across mechanisms.
Early binding vs. late binding
When structure is committed; determines where the failure surfaces, at write time or read time.
Space vs. time
For example: compression, indexing, materialization, caching.
Isolation vs. utilization
For example: multi-tenancy, resource pools, spot capacity.
Simplicity vs. adaptivity
For example: static plans vs. adaptive execution.
Fail-fast vs. fail-degraded
Reject or continue when something is wrong.
Show all 10 trade-offsShow fewer
Latency vs. throughput
For example: batching, group commit, micro-batch.
Precompute vs. compute-on-read
For example: materialized views, ELT vs. ETL, schema-on-read.
Generality vs. specialization
For example: general engines vs. purpose-built structures.
Durability vs. acknowledgment latency
How much must be confirmed before you say "yes".
Cost as a substitutable resource
Money buys position on every other axis; the only axis denominated in a different unit.
Ideas this mechanism meets that keep coming back, mechanism after mechanism.
Variance costs more than volume
Variance costs more than volume.
Granularity mismatch
Granularity mismatch creates amplification.
Immutable storage grows indirection
An immutable substrate grows an indirection layer plus a collector.
Fixed capacity meets growth
A fixed-capacity structure meeting growing cardinality breaks the design.
Costs are conserved
Costs are conserved, never removed.
Show all 12 principlesShow fewer
Independence is assumed
Independence is assumed everywhere and is true almost nowhere.
The end-to-end argument
Guarantees compose to the weakest link, and only endpoint guarantees are real.
Queues explode near full load
Response time explodes as utilization nears 100%.
The algebra decides
What can be skipped, pushed down, parallelized, merged or precomputed is determined by the algebra of the function, not the layout of the data.
Which way the error points
Sound approximations
When exactness is too expensive, use a sound approximation.
Little's Law
Concurrency = Throughput × Latency.