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.