erlang 内存被大量占用,跟踪过程

第一步:
查看进程数目是否正常? erlang:system_info(process_count).



第二步:
查看节点的内存消耗在什么地方?
> erlang:memory().
[{total,2099813400},
  {processes,1985444264},
  {processes_used,1985276128},
  {system,114369136},
  {atom,4479545},
  {atom_used,4477777},
  {binary,22756952},
  {code,10486554},
  {ets,47948808}]
显示内存大部分消耗在进程上,由此确定是进程占用了大量内存



第三步:
查看哪些进程占用内存最高?
> spawn(fun() -> etop:start([{output, text}, {interval, 1}, {lines, 20}, {sort, memory}]) end).
(以输出text方式启动etop,其间隔为1秒,输出行数为20行,按照内存排序. 这里spawn一个新进程,目的是输出etop数据时不影响erlang shell 输入.)



第四步:
查看占用内存最高的进程状态
> erlang:process_info(pid(0,12571,0)).          
[{current_function,{mod_player,send_msg,2}},
  {initial_call,{erlang,apply,2}},
  {status,waiting},
  {message_queue_len,0},
  {messages,[]},
  {links,[<0.12570.0>]},
  {dictionary,[]},
  {trap_exit,false},
  {error_handler,error_handler},
  {priority,normal},
  {group_leader,<0.46.0>},
  {total_heap_size,12538050},
  {heap_size,12538050},
  {stack_size,10122096},
  {reductions,3795950},
  {garbage_collection,[{min_bin_vheap_size,46368},
                       {min_heap_size,233},
                       {fullsweep_after,65535},
                       {minor_gcs,0}]},
  {suspending,[]}]

其中” {total_heap_size,12538050},” 表示占用内存为 12358050 words(32位系统word size为4,64位系统word size为8, 可以通过erlang:system_info(wordsize) 查看),在64位系统下将近100M, 太夸张了!



第五步:
手动gc回收,希望问题可以解决
>  erlang:garbage_collect(pid(0,12571,0)).
true
再次查看进程内存,发现没有任何变化!gc没有回收到任何资源,因此消耗的内存还在发挥作用,没有回收!





第六步:
不要怀疑系统,首先要怀疑自己的代码
认真观察代码,其大致结构如下:
send_msg(Socket, Pid) ->
    try
         receive
             {send, Bin} ->
                 ...
             {inet_reply, _Sock, Result} ->
                ...
    catch
        _:_ ->
            send_msg(Sock, Pid)
    end.
其目的是循环等待数据,然后进行发送,其使用了try...catch捕获异常.
这段代码有问题么?
对,这段代码的确有问题, 其不是尾递归! try...catch会在stack中保存相应的信息,异常捕获需要放置在函数内部,所以send_msg最后调用的是try...catch,而不是自身,所以不是尾递归!
可以通过代码得到验证:
  cat test.erl
-module(test).
-compile([export_all]).


t1() ->
    Pid = spawn(fun() -> do_t1() end),
    send_msg(Pid, 100000).

t2() ->
    Pid = spawn(fun() -> do_t2() end),
    send_msg(Pid, 100000).

send_msg(_Pid, 0) ->
    ok;
send_msg(Pid, N) ->
    Pid ! <<2:(N)>>,
    timer:sleep(200),
    send_msg(Pid, N-1).

do_t1() ->
    erlang:garbage_collect(self()),
    Result = erlang:process_info(self(), [memory, garbage_collection]),
    io:format("~w ~n", [Result]),
    io:format("backtrace:~w~n~n", [erlang:process_display(self(), backtrace)]),
    try
      receive
          _ ->
              do_t1()
      end
    catch
      _:_ ->
          do_t1()
    end.

do_t2() ->
    erlang:garbage_collect(self()),
    Result = erlang:process_info(self(), [memory, garbage_collection]),
    io:format("~w ~n", [Result]),
    io:format("backtrace:~w~n~n", [erlang:process_display(self(), backtrace)]),
    receive
      _ ->
          do_t2()
    end.

版本1:erlc test.erl && erl -eval "test:t1()"
版本2:erlc test.erl && erl -eval "test:t2()"
你会看到版本1代码的调用堆栈在不断增长,内存也在增长, 而版本2函数调用地址保持不变,内存也没有发生变化!

总结:
1,服务器编程中,循环一定确保为尾递归
2,善于使用OTP,如果使用gen_server替换手写loop,就不会出现这个问题!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Erlang fprof是一个用于性能分析的工具,可以帮助开发者发现并优化Erlang程序中的性能问题。它能够提供详细的函数调用统计信息,帮助开发者找到运行时间最长的函数、占用内存最多的函数等。 使用Erlang fprof进行性能分析非常简单。首先,我们需要在代码中插入一些跟踪代码,以便记录每个函数的运行时间。然后,我们运行程序,并使用fprof:start()函数开启fprof的跟踪功能。接下来,我们可以使用fprof:analyse()函数来生成性能分析报告。报告中包含了各个函数的运行时间、内存使用情况等统计信息。 性能分析报告包含的信息能够指导开发者找到程序的瓶颈所在。通过查看报告中运行时间最长的函数,我们可以确定哪些函数需要进行性能优化。通过查看报告中内存使用最多的函数,我们可以确定哪些函数占用了过多的内存,需要进行内存优化。 除了生成性能分析报告外,Erlang fprof还具备其他有用的功能。例如,我们可以使用fprof:trace/2函数在特定的函数或模块中进行跟踪,以便详细了解函数的调用关系。我们还可以使用fprof:pause()和fprof:resume()函数来暂停和恢复fprof的跟踪。 总而言之,Erlang fprof是一个强大的性能分析工具,能够帮助开发者发现和解决Erlang程序中的性能问题。通过使用fprof,我们可以找到性能瓶颈并进行相应的优化,提升程序的运行效率和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值