Home > Erlang探索, 源码分析, 调优 > 调查使用binary最多TOPN进程

调查使用binary最多TOPN进程

October 15th, 2013

原创文章,转载请注明: 转载自系统技术非业余研究

本文链接地址: 调查使用binary最多TOPN进程

Erlang程序是非常健壮的,通常一个典型的虚拟机里面跑很多进程,这些进程即使有bug,按照erlang的哲学是快速死掉,系统留下异常堆栈,很容易发现问题。照理说erlang是很少crash的,但实际情况不是这样的。

在erlang VM crash的案例中,我们会发现大部分的网络服务器的原因都是binary内存不够申请不出来,所以内存短缺是最致命的影响稳定的因素。通常设计良好的erlang程序,按照otp的设计哲学不会占用太多内存的,即使占用了gc也很快就会回收的,除了binary这个内存使用大户。

比如说网络服务器程序,我们用binary来保存用户的封包,我们无法预测用户要发送多大的包,比如上限是50M,如果我们的系统有1000个这样的用户,在极端情况下,我们是要耗用50G内存的。通常在这种情况下,我们拿不出这么多物理内存,然后crash就很大概率会发生。

调查哪些进程用掉了最多的binary内存就很有必要了,我们在极端情况下,可以选择性的杀掉这些内存,保护自己不至于毁灭。

erlang:process_info有个未公开的选项 binary用来获取这个进程拥有的binary情况。

我们看下它的实现:

static Eterm
bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh)
{
    struct erl_off_heap_header* ohh;
    Eterm res = NIL;
    Eterm tuple;

    for (ohh = oh->first; ohh; ohh = ohh->next) {
        if (ohh->thing_word == HEADER_PROC_BIN) {
            ProcBin* pb = (ProcBin*) ohh;
            Eterm val = erts_bld_uword(hpp, szp, (UWord) pb->val);
            Eterm orig_size = erts_bld_uint(hpp, szp, pb->val->orig_size);

            if (szp)
                *szp += 4+2;
            if (hpp) {
		Uint refc = (Uint) erts_smp_atomic_read_nob(&pb->val->refc);
                tuple = TUPLE3(*hpp, val, orig_size, make_small(refc));
                res = CONS(*hpp + 4, tuple, res);
                *hpp += 4+2;
            }
        }
    }
    return res;
}


Eterm
process_info_aux(Process *BIF_P,
                 Process *rp,
                 Eterm rpid,
                 Eterm item,
                 int always_wrap)
{
 ...
   case am_binary: {
        Uint sz = 3;
        (void) bld_bin_list(NULL, &sz, &MSO(rp));
        hp = HAlloc(BIF_P, sz);
        res = bld_bin_list(&hp, NULL, &MSO(rp));
        break;
    }
...
}

这个选项会返回一个bin情况tuple的列表,每个tuple的第一个是binary的地址,第二个是大小,第三个是引用次数。

我们来演示下如何使用:

$ erl
Erlang R15B02 (erts-5.9.2) [source] [64-bit] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9.2  (abort with ^G)
1> X= <<0:1024>>.
<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,...>>
2> Y= <<0:2048>>. 
<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,...>>
3> Z= <<0:256>>. 
<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,...>>      
4> erlang:process_info(self(), binary).
{binary,[{140379060495896,128,5},
         {140379060500136,256,4},
         {140379060500896,32,3}]}
5> 

三个binary信息和我们预想的是符合的。

最后我们用这个选项来做个更智能的,调查使用binary最多TOPN进程,代码如下:

topN(N)->
        [{M, P, process_info(P, [registered_name, initial_call,current_function, dictionary]), B} ||
        {P, M, B} <- lists:sublist(lists:reverse(lists:keysort(2,processes_sorted_by_binary())),N)].

processes_sorted_by_binary()->
     [case process_info(P, binary) of
              {_, Bins} ->
                 SortedBins = lists:usort(Bins),
                 {_, Sizes, _} = lists:unzip3(SortedBins),
                 {P, lists:sum(Sizes), []};
              _ ->
                {P, 0, []}
         end ||P <- processes()].

没事调用下,查查谁在消耗binary内存,有助于发现问题。

祝玩得开心!

Post Footer automatically generated by wp-posturl plugin for wordpress.

Comments are closed.