操作系统内存与Erlang虚拟机内存不符问题排查求助
嘿,咱们来好好拆解这个问题——Erlang虚拟机报告的内存和系统实际占用完全不符,还时不时重启,这确实是个棘手的场景。我来帮你梳理排查方向和解决方案:
一、先理清内存统计的矛盾点
你给出的Erlang内存快照有个明显的异常:total显示约3.3GB,但system字段居然高达9.3GB,正常情况下total应该等于processes + system的总和(350MB + 9.3GB ≈9.65GB,远大于3.3GB)。先确认这个快照的准确性:
- 是不是直接执行
erlang:memory().的输出?有没有可能是多次调用的结果拼错了? - OTP 21里的
system包含了binary、code、ets、atom以及VM内部结构的内存开销,正常逻辑下total必然是进程内存加系统内存的总和,所以这个数据矛盾本身就是重要线索。
二、核心排查方向
1. 检查Erlang端口(Port)的内存占用
你已经排查了进程,但端口是很容易被忽略的点:Erlang端口用于和外部程序(比如OS命令、C扩展、网络套接字)交互,它们的内存不统计在processes里,而是算进system。如果有端口泄漏,或者某个端口持续处理大数据却没正确释放,会导致system内存暴涨。
- 用
erlang:ports().列出所有端口,再逐个执行erlang:port_info(Port, memory).查看内存占用; - 在OS层面用
lsof命令查看Erlang进程关联的文件描述符、子进程,排查是否有异常的外部进程占用大量内存。
2. 排查内存分配器的碎片与溢出
OTP 21默认用jemalloc做内存分配,频繁分配/释放大内存块时,分配器可能出现严重碎片,导致OS看到的常驻内存(RSS)远高于VM报告的total——因为分配器没把闲置内存还给系统,造成“伪泄漏”的假象。
- 启动VM时加上
+M true参数启用分配器统计,然后用erlang:system_info(memory_allocators).查看状态; - 在OS层面用
ps aux --sort=-rss看Erlang进程的RSS值,如果RSS远大于VM的total,说明分配器碎片问题严重,或者存在VM之外的内存溢出。
3. OS层面的内存占用排查
有时候问题不在Erlang应用,而是OS本身的其他进程或内核模块占用了内存:
- 用
free -h查看系统内存,重点看buff/cache和swap:如果swap被大量使用,说明物理内存不足,应用很可能被OOM Killer强制杀死(这就是你看到的随机重启的原因); - 用
htop或top按内存排序,检查是否有其他进程(比如数据库、日志服务、监控工具)占用大量内存; - 核对内核参数:比如
vm.swappiness过高会导致频繁换页,overcommit_memory的设置可能影响Erlang内存分配(OTP推荐设为0或1)。
4. OTP 21的已知bug排查
OTP 21是2018年发布的老版本,存在不少已知的内存泄漏或统计错误bug:
- 比如某些版本在频繁更新大ETS表时会出现内存泄漏,或者端口内存统计错误;
- 检查是否涉及未修复的bug(比如OTP-15467、OTP-14998),如果你的OTP 21是旧补丁版本,升级到OTP 21的最新补丁版(比如21.3.8.24)可能解决问题。
三、判断问题归属
从目前信息来看,大概率是应用层或OTP版本的问题,但也不排除OS资源不足的可能:
- 如果OS层面没有其他高内存进程,且Erlang进程的RSS远大于VM报告的
total,问题出在Erlang应用(端口泄漏、分配器碎片、OTP bug); - 如果OS层面有其他进程占用大量内存,或者swap被耗尽,那就是OS资源不足或其他进程干扰导致的。
四、具体解决建议
- 先验证内存快照准确性:重新执行
erlang:memory().,确保数据正确,排除粘贴错误; - 定位端口内存问题:用
erlang:port_info/2逐个检查端口,找到高内存端口后,排查对应的业务逻辑(比如是否有未关闭的外部连接、大量二进制数据堆积); - 优化内存分配器:如果是碎片问题,尝试切换到系统malloc(启动VM加
+M allocator system参数),或者直接升级到OTP 22+(OTP 22对内存分配器做了大幅优化); - 检查OOM日志:Ubuntu的OOM Killer日志在
/var/log/syslog里,搜索Out of memory,确认是不是Erlang进程被强制杀死,这能明确重启的原因; - 升级OTP版本:如果是OTP 21的bug,优先升级到该版本的最新补丁,或者直接迁移到OTP 24/25这类长期支持版本,能解决很多已知内存问题;
- 调整OS内核参数:设置
vm.swappiness=10减少不必要的换页,vm.overcommit_memory=1允许内存过度提交(适配Erlang的内存模型),同时关闭不必要的后台进程,确保系统有足够物理内存。
内容的提问来源于stack exchange,提问作者Hitesh Vaghani




