Erlang内存吃紧之解决思路

时间:2022-06-01 12:59:03

首先使用erlang:memory()确定是哪个部分内存吃紧,根据输出的内容,比对内存占用大小,有针对性地进行分析。在erlang系统里内存的单位为word,通过erlang:system_info(wordsize)接口可以看到一个word占用多少个字节。如32位系统是4字节,64位系统是8字节。

> memory().
[{total,13079568},
{processes,4214248},
{processes_used,4213320},
{system,8865320},
{atom,202481},
{atom_used,189725},
{binary,52800},
{code,4618749},
{ets,263848}]
> erlang:system_info(wordsize).
8

1.进程占用过多内存的情况(processes值较大)

可用etop查找内存占用高的进程,也可以排序所有进程(erlang:processes/0可获得所有进程pid)的内存占用(erlang:process_info(Pid, heap_size)可获得进程内存占用)。

找到目标进程后分析该进程的信息process_info(Pid)进一步发现问题,通常找到目标进程,就能从代码和进程状态中分析出问题。可能的问题有:

是否陷入非尾递归的死循环?(如果一直吃CPU不吃内存则可能是尾递归的死循环导致)

进程的主循环是否没用尾递归,导致调用栈无限增长?

是否存入过多不必要的数据到进程字典中且没有及时erase?

gen_server的state中是否存入过多内容?

etop memory示例:

> spawn(fun() -> etop:start([{sort, memory}]) end).
<0.34.0> ========================================================================================
nonode@nohost 11:50:35
Load: cpu 0 Memory: total 12642 binary 29
procs 28 processes 4076 code 4454
runq 0 atom 198 ets 256 Pid Name or Initial Func Time Reds Memory MsgQ Current Function
----------------------------------------------------------------------------------------
<0.7.0> application_controll '-' 7270 426440 0 gen_server:loop/6
<0.12.0> code_server '-' 98774 142688 0 code_server:loop/1
<0.26.0> erlang:apply/2 '-' 9840 122072 0 shell:get_command1/5
<0.3.0> erl_prim_loader '-' 181804 62856 0 erl_prim_loader:loop
<0.23.0> user_drv '-' 4122 26496 0 user_drv:server_loop
<0.0.0> init '-' 2347 24520 0 init:loop/1
<0.32.0> erlang:apply/2 '-' 1668 21424 0 shell:eval_loop/3
<0.11.0> kernel_sup '-' 1543 12152 0 gen_server:loop/6
<0.25.0> group:server/3 '-' 1613 11864 0 group:more_data/5
<0.6.0> error_logger '-' 227 6904 0 gen_event:fetch_msg/
========================================================================================
>etop:stop().

2.ets表占用过多内存的情况(ets值较大)

排序所有ets表(ets:all/0可获得所有ets表名Tab)的内存占用(ets:info(Tab, memory)可获得内存占用),找出最占内存的ets表进行分析。ets表过大,是因为insert过多内容,却很少delete。分析表的功能,寻求恰当的方式做优化,节源开流。

ets表内存战斗排序示例:

> lists:sublist(lists:reverse(lists:keysort(2,[{T, ets:info(T, memory)} || T <- ets:all()])), 10).
[{1,11220},
{4098,7022},
{ac_tab,942},
{inet_db,497},
{global_locks,302},
{global_names,302},
{global_names_ext,302},
{global_pid_names,302},
{global_pid_ids,302},
{inet_cache,302}]

一些优化的思路:

定期将不常用数据清理出内存

选择更优的数据结构(如选择binary存字符串,而不是list)

优化掉冗余的数据(如相同数据拷贝多份的情况,可以优化成只保存一份数据

PS:本文非权威,仅为个人思考总结