BEAM (Erlang virtual machine)
The virtual machine in Erlang/OTP that runs Erlang and Elixir: millions of lightweight processes, each with its own heap and garbage collection, preemptively scheduled across all cores, with code that can be replaced while the system runs.
- What it is
- Runtime virtual machines
- Type
- Open source
- Made by
- Ericsson (Erlang/OTP team)
- License
- Apache 2.0
- Facts checked
- 8 October 2026
What it is
The Erlang and Elixir compilers produce .beam files of BEAM instructions, and the runtime system loads them module by module. Since Erlang/OTP 24, a JIT called BeamAsm translates each instruction to native code as a module loads, on x86-64 and AArch64; elsewhere an interpreter runs them. The unit of work is the process: not an operating system process or thread but a lightweight one owned by the runtime. A newly spawned process uses 327 words of memory (about 2.6 KB on a 64-bit machine), including a 233-word heap that grows as needed, so a system can hold hundreds of thousands or millions of them.
By default the runtime starts one scheduler thread per logical processor. Each process runs until it has used its budget of reductions, roughly one per function call; after 4,000 the scheduler switches it out, so a busy process can’t starve the others. Processes share no memory: data in a message is copied from the sender’s heap to the receiver’s (large reference-counted binaries are the exception). In exchange, every process has its own heap and its own generational copying garbage collector, so collection works on one small heap at a time.
Code can be replaced in a running system, one module at a time. The runtime keeps two versions of a module, current and old; processes move to the new code when they next make a fully qualified call, and loading a third version purges the oldest and terminates any processes still running it.
Why teams pick it
- Processes are cheap (327 words when spawned), so one per connection, request or device is normal
- Preemptive scheduling by reductions keeps latency fair: no process can hold a scheduler for long running Erlang code
- Per-process heaps and garbage collection: each collection touches only one process’s memory
- Hot code loading upgrades modules without stopping the system, the property Erlang was built for in telephone switches
Watch out for
- Messages are copied between heaps, so passing large data structures between processes costs time and memory
- Native code called through NIFs isn’t preempted, and a slow NIF can block a whole scheduler
- BeamAsm does hardly any optimisation across instructions, so tight CPU-bound code gains less from it than from an optimising JIT
- Only two versions of a module can be loaded at once; a third load kills processes still running the oldest
Alternatives
What all runtime virtual machines have in common →How it fits with other technologies
Measure a fresh process in the Erlang shell (64-bit; OTP 27 and 28 print the same) · erlang
1> Fun = fun() -> receive after infinity -> ok end end. % a process that just waits
2> {_, Bytes} = process_info(spawn(Fun), memory).
{memory,2616}
3> Bytes div erlang:system_info(wordsize).
327See it in a real stack
No architecture uses BEAM (Erlang virtual machine) yet. Drop it into the playground and wire it up yourself.Open playground →Where it came from1986–2021
Erlang was designed at the Ericsson Computer Science Laboratory from 1986 for telephone switches: highly concurrent programs that had to run for years and be upgraded while running. The first version was an interpreter written in Prolog, far too slow for real products. In 1989 Joe Armstrong designed JAM (Joe’s Abstract Machine), a compact byte-coded machine inspired by the Warren Abstract Machine for Prolog; Mike Williams wrote its runtime in C, Armstrong the compiler and Robert Virding the libraries, and it ran about 70 times faster than the Prolog interpreter.
Bogumil Hausman then built TEAM, which compiled Erlang to C, and after it BEAM (Bogdan’s Erlang Abstract Machine), a hybrid that could run compiled C code or threaded code in an interpreter. Björn Gustavsson, who joined the Erlang/OTP team at the end of 1996, later took over BEAM and argued that the compile-to-C path “had to die”: it came in three flavours and caused bugs even in the threaded interpreter. BEAM became the machine that runs Erlang/OTP.
1998 was a turning point. Ericsson announced the AXD301 switch in March, possibly the largest program ever written in a functional language, but Ericsson Radio had banned Erlang for new products in February. The ban led indirectly to Open Source Erlang, released in December. Elixir 1.0 (2014) brought a second large language to the VM, and BEAM gained a JIT compiler, BeamAsm, in Erlang/OTP 24 on 12 May 2021.
Why it’s built this way
- Processes belong to the language, not the operating system
- A telephone switch handles tens or hundreds of thousands of simultaneous calls. Lightweight processes with no shared memory, created and scheduled by the runtime, made one process per activity affordable.
- A separate heap per process
- JAM and BEAM gave every process its own heap. Virding’s experimental VEE used one shared heap, which made message passing much faster, but overall it was no quicker, probably because the shared heap lowered the cache hit rate.
- Preempt by counting reductions
- A process can only be suspended at safe points such as function calls and receives. Because Erlang has no loops except recursion, counting calls gives every process a bounded time slice: preemptive scheduling built on cooperative switch points.
- Two versions of each module in memory
- Systems that run forever must change code without stopping. Keeping old and current code side by side lets running processes finish in the old version and move to the new one on their next fully qualified call.
Timeline
- 1986
Erlang work begins at Ericsson’s Computer Science Laboratory
- 1989
JAM, the first Erlang virtual machine, replaces the Prolog interpreter
- 1996
First Erlang/OTP release, R1B
- 1998
Erlang released as open source in December
- 2021
Erlang/OTP 24 adds the BeamAsm JIT on 12 May
- Joe Armstrong: A History of Erlang (HOPL III, 2007)
- Erlang/OTP blog: A Brief History of the BEAM Compiler (JAM, TEAM, BEAM, VEE; OTP R1B in 1996)
- Erik Stenman: The BEAM Book, chapter 10 Scheduling (reductions, preemption at function calls)
- Erlang/OTP blog: Erlang/OTP 24 Highlights (12 May 2021; BeamAsm)
- Elixir blog: Elixir v1.0 released (18 September 2014)
Sources10 · facts checked 8 October 2026
- Erlang/OTP docs: Compilation and Code Loading (BEAM, .beam files, current and old code, purging)
- Erlang/OTP docs: BeamAsm, the Erlang JIT (x86-64 and aarch64; hardly any cross-instruction optimisation)
- Erlang/OTP docs: Processes reference (lightweight processes)
- Erlang/OTP docs: Efficiency Guide, Processes (327 words, 233-word heap, millions of processes, copied messages)
- Erlang/OTP docs: Erlang Garbage Collector (per-process generational semi-space copying collector)
- Erlang/OTP docs: erlang:bump_reductions/1 (one reduction per call; switch after 4,000)
- Erlang/OTP docs: erl, the +S flag (scheduler threads default to logical processors)
- Erik Stenman: The BEAM Book (scheduling, reductions, NIFs that block a scheduler)
- Elixir docs: Code (modules compiled to bytecode in .beam files)
- erlang/otp README (Apache License 2.0; copyright Ericsson AB)