Erlangserver緊記憶體最佳化解決方案

來源:互聯網
上載者:User

標籤:

提出的問題:server100萬人線上,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,server編程中,迴圈一定確保為尾遞迴
2,盡量使用OTP,假設使用gen_server更換手寫loop,將避免這個問題


著作權聲明:本文部落格原創文章,部落格,未經同意,不得轉載。

Erlangserver緊記憶體最佳化解決方案

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.