BEAM Internals & Performance

erl_crash.dump is written on VM crash (out of memory, erlang:halt("reason")).

38.1 Architecture

Schedulers: one per CPU core (+S), each with its own run queue; work stealing balances load.

Reductions: each process runs for ~4000 reductions (roughly function calls) before being preempted → fair, soft real-time latency.

Dirty schedulers: for long NIFs and blocking I/O.

Async I/O threads / poll sets for ports and sockets.

JIT (BeamAsm): since OTP 24, BEAM bytecode is translated to native x86-64/ARM64 code at load time.

38.2 Memory

Each process has its own heap and stack (grow toward each other) and its own generational GC — no global stop-the-world pauses.

Messages are copied to the receiver's heap (except refc binaries and literals); message_queue_data = off_heap reduces GC cost for busy mailboxes.

Atoms live in a global table, never collected.

Literals (constants in code) are shared in a literal area.

erlang:memory(),
erlang:system_info(process_count), erlang:system_info(atom_count),
erlang:garbage_collect(Pid),
erlang:statistics(run_queue), erlang:statistics(reductions),
erlang:process_info(Pid, [heap_size, total_heap_size, reductions]).

38.3 Performance guidelines (from the Efficiency Guide)

Measure first: timer:tc/1, eprof, fprof, tprof, benchmarks with erlperf.

Use maps for key/value data; records for fixed internal structures.

Avoid ++ in loops, and length/1 in guards on long lists.

Use iolists instead of concatenating binaries/strings.

Binary matching is optimised — match binaries in function heads; avoid binary_to_list.

Keep messages small; pass refs/ids instead of big terms; large shared data → ETS or persistent_term.

Avoid single-process bottlenecks; shard/partition state.

Watch mailbox sizes; selective receive on large mailboxes is O(n) (except the ref-trick optimisation for make_ref/monitor patterns).

Use ets with read_concurrency/write_concurrency appropriately.

Tune the VM: +S, +sbwt, +A, +K, +P, +Q, +hms, +MBas.

38.4 Crash dumps

erl_crash.dump is written on VM crash (out of memory, erlang:halt("reason")). Inspect with crashdump_viewer.