• Erlang服务器内存吃紧的优化解决方法


      

    问题提出:服务器100万人在线,16G内存快被吃光。玩家进程占用内存偏高

    解决方法:

    第一步:
    erlang:system_info(process_count). 查看进程数目是否正常,是否超过了erlang虚拟机的最大进程数。
    第二步:
    查看节点的内存瓶颈所在地方
    > 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:erlctest.erl && erl -eval "test:t1()"
    版本2:erlctest.erl && erl -eval "test:t2()"
    你会看到版本1代码的调用堆栈在不断增长,内存也在增长, 而版本2函数调用地址保持不变,内存也没有发生变化!

    总结:
    1,服务器编程中,循环一定确保为尾递归;
    2,尽量使用OTP,如果使用gen_server替换手写loop,就会避免出现该问题。

     转自:http://www.2cto.com/os/201405/299927.html 

  • 相关阅读:
    javaScript中的find()方法和返回数据的内存指向
    高级函数 filter map reduce 的使用
    for ... in and for ... of 理解
    git 解决冲突问题
    H5内唤醒百度、高德APP
    HTML 5标准中最新引入的template标签介绍
    jquery选择器使用
    ajax封装函数
    常用正则表达式
    JS-----事件、image对象
  • 原文地址:https://www.cnblogs.com/unqiang/p/4170054.html
Copyright © 2020-2023  润新知