Memory and runtime
How a language reclaims memory and how it reaches the processor are the two axes with the most direct effect on deployment: they determine start-up time, pause behaviour, binary size, and what the program can promise about latency.
Memory management models¶
Five models, ordered from least to most automatic. Several languages combine two: C++ pairs manual allocation with RAII, and Python pairs reference counting with a cycle collector.
| Model | Mechanism | Representative languages | What it buys | What it costs |
|---|---|---|---|---|
| Manual | The program allocates and frees explicitly. | C, Zig, assembly | No runtime overhead; allocation cost is fully visible | Use-after-free, double free, and leaks are program errors |
| RAII / scope-bound | Resources are released when the owning object leaves scope. | C++, Ada, Rust | Deterministic release without a collector | Cycles still leak unless broken explicitly |
| Reference counting | Each object counts its live references and frees at zero. | Swift, Objective-C, Python, PHP, Perl | Deterministic destruction and low pause times | Cycles leak; count updates cost on every copy |
| Tracing garbage collection | A collector periodically finds and frees unreachable objects. | Java, C#, Go, Haskell, JavaScript | Cycles are collected; allocation is cheap | Pauses and memory overhead; timing is not under program control |
| Ownership and borrowing | A compile-time discipline proves each value has one owner and no dangling borrows. | Rust | Safety of a collector with the cost model of manual memory | Steeper learning curve; some structures need escape hatches |
Sources: language and runtime documentation. Cost columns describe documented behaviour of the reference implementation, not benchmark results.
Reference cycles¶
The defining limitation of counting
Reference counting reclaims an object the moment its last reference disappears, which makes destruction deterministic and pauses short. It cannot, however, reclaim a group of objects that reference each other, because every count in the group stays above zero.
Languages that count therefore give the programmer a way to declare an edge non-owning. Swift provides
weak and unowned; Objective-C provides the same pair; Python and PHP instead run
a secondary cycle collector so that the programmer need not annotate.
Tracing collectors have the opposite profile: cycles are free, but destruction time is decided by the collector rather than the program. That is why latency-sensitive systems either avoid allocation on the hot path or choose a counted or owned model.
See Memory management in the Swift article for a worked example of a cycle and its resolution.
Execution models¶
How source reaches the processor. The axis records the route taken by the reference implementation; alternative implementations are noted in each language entry.
| Model | Mechanism | Representative languages | What it buys | What it costs |
|---|---|---|---|---|
| Ahead-of-time compilation | Source is translated to machine code before deployment. | C, C++, Rust, Go, Swift, Haskell | Fast start-up, no runtime compiler, small deployable surface | Per-target builds; no runtime specialisation |
| Bytecode with JIT | Source compiles to a portable bytecode that a runtime compiles again while running. | Java, C#, Kotlin, Scala, JavaScript | Portable artefacts; profile-guided specialisation at runtime | Warm-up period and a large runtime to ship |
| Bytecode interpretation | A virtual machine executes bytecode without translating it to machine code. | CPython, Ruby, Lua, PHP | Simple, portable, and quick to start | Interpretation overhead per instruction |
| Transpilation | Source is translated to another high-level language and executed by that language's toolchain. | TypeScript, Elm, ClojureScript | Reuses a mature host platform and its ecosystem | Debugging crosses a translation boundary; host semantics leak through |
| Query planning | A declarative statement is compiled into an execution plan chosen by a cost model. | SQL, Datalog | The engine may re-plan as data changes | Performance depends on statistics the author does not control |
A single language may appear under more than one model across implementations: Python is interpreted by CPython and JIT-compiled by PyPy; Kotlin targets both the JVM and native code.
Deployment consequences¶
Start-up latency
AOT binaries start in milliseconds. JIT runtimes need a warm-up period before reaching peak throughput, which matters most for short-lived processes.
Tail latency
Tracing collectors introduce pauses whose length depends on live-set size. Counted and owned models trade that for steady per-operation cost.
Artefact size
A managed language ships its runtime; an AOT language ships a self-contained binary but one build per target platform.
Interoperability
Languages with a C-compatible ABI can be called from almost anywhere; languages with a managed runtime interoperate best within their own platform.