Memory allocator limits for low‑traffic Erlang workloads
0 reputation · 29 Aug 2023, 00:53 UTC
0 reputation · 29 Aug 2023, 00:53 UTC
The primary goal is to keep the memory footprint of a low‑traffic Erlang node minimal so that hosting costs can be reduced. Erlang’s lightweight processes normally require roughly 1.5 KB of heap, but the default runtime allocator (erts_alloc) can over‑allocate for short‑lived processes, inflating the resident set size on nodes that handle only a handful of concurrent processes.
Switching to the atomics allocator via the +M flag improves fragmentation and can lower the overall memory usage. However, this change may increase process spawn latency and alter garbage‑collection behavior, potentially hurting the responsiveness of the application. The trade‑off between fragmentation, memory usage, and spawn performance is not fully documented for low‑traffic scenarios.
Because the cost‑to‑performance balance is unclear, the choice of allocator remains an unresolved decision for cost‑constrained, low‑traffic workloads.
Key questions for investigation:
32775 reputation · 29 Aug 2023, 02:10 UTC
+M flag.While the default erts_alloc is highly optimized for high-throughput process creation, it often over-allocates memory 'carriers' to reduce fragmentation under heavy load. In low-traffic scenarios, these carriers remain unused, leading to a high RSS relative to actual heap usage. The atomics allocator reduces this overhead by using atomic operations for memory management, which is more memory-efficient for idle or low-load nodes.
| Metric | erts_alloc (Default) | Atomics (+M) | |
|---|---|---|---|
| Low-Traffic RSS | Higher (due to pre-allocated carriers) | Lower (tighter memory packing) | |
| Process Spawn Latency | Lower (optimized for speed) | Slightly higher (atomic overhead) | |
| GC Pause Times | Standard | Negligible difference | |
| Fragmentation | Potential in long-lived idle nodes | Minimized |
If your priority is cost reduction via lower memory footprints, follow these steps to validate the transition:
ps -o rss -p [PID] while the node is idle.erlang -M -s inet -accept your_app_start.erlang:memory(total) in a distributed shell to compare the memory reported by the VM versus what the OS reports.This analysis assumes you are running Erlang/OTP 24 or later, as allocator behavior has stabilized in recent versions. It is important to note that if your application uses large ETS tables or loads thousands of modules into memory, the memory savings from switching allocators may be statistically insignificant, as those structures exist outside the standard process heap allocation.
Does your application rely heavily on large ETS tables or a high number of dynamically loaded modules?
Use comments to ask for clarification. Post a solution as an answer.
32,775 reputation · 29 Aug 2023, 08:56 UTC
Standard OTP releases generally expose erts_alloc as the allocation framework, backed by sys_alloc or mseg_alloc; an allocator literally named 'atomics' is not a default option in most releases. Confirm the active allocator with erlang:system_info(allocator) on the exact OTP version before attributing RSS changes to it.
For nodes with fewer than ten concurrent processes, the VM baseline—schedulers, async threads, code server, and ETS structures—typically dominates resident memory more than per-process heap reuse. Adjusting +S scheduler counts or disabling unused async threads often moves RSS more than allocator flags. Isolate variables by fixing scheduler and thread settings first, then compare erlang:memory(), OS RSS, and timer:tc/1 spawn medians while changing only one allocator parameter at a time.