[Erlang 0012]Erlang Process input queue 一文我们提到使用erlang:process_info/1抓取进程运行时状态信息,有时我们需要把节点内所有的进程的信息都抓取下来,便于分析整个节点内进程的运行状态,特别是挑出一些异常的进程:比如有些进程的reduction值和其它进程相比大出好几个数量级,那这个进程需要好好检查一下了。

    下面的代码就是把当前节点内所有进程遍历一遍,把进程状态写入到文本里面:

process_infos() ->          
    filelib:ensure_dir("./log/"),
    File = "./log/processes_infos.log",
    {ok, Fd} = file:open(File, [write, raw, binary, append]), 
    Fun = fun(Pi) ->
                   Info = io_lib:format("=>~p \n\n",[Pi]),
                  case  filelib:is_file(File) of
                        true   ->   file:write(Fd, Info);
                        false  ->
                            file:close(Fd),
                            {ok, NewFd} = file:open(File, [write, raw, binary, append]),
                            file:write(NewFd, Info)
                     end,
                     timer:sleep(20)
                 end,
    [   Fun(erlang:process_info(P)) ||   P <- erlang:processes()].      
 
运行一下看看,我们还要学会读这些数据:
[{registered_name,rex},
   {current_function,{gen_server,loop,6}},
   {initial_call,{proc_lib,init_p,5}},
   {status,waiting},
   {message_queue_len,0},
   {messages,[]},
   {links,[<0.10.0>]},
   {dictionary,[{'$ancestors',[kernel_sup,<0.9.0>]},
                {'$initial_call',{rpc,init,1}}]},
   {trap_exit,true},
   {error_handler,error_handler},
   {priority,normal},
   {group_leader,<0.8.0>},
   {total_heap_size,28657},
   {heap_size,10946},
   {stack_size,9},
   {reductions,13905},
   {garbage_collection,[{min_bin_vheap_size,10946},
                        {min_heap_size,10946},
                        {fullsweep_after,65535},
                        {minor_gcs,2}]}, %%% erlang的垃圾回收是mark sweep方式。 major做全量的gc, minor就是不够内存的时候,缺多少就去回收下,力度比较小。
   {suspending,[]}]
 
这些字段的含义:
 
{current_function, {Module, Function, Args}}

当前进程调用的方法M F A

{dictionary, Dictionary}

当前进程的进程字典数据,调试过程中我们可以为进程添加一些特殊标记来识别进程

{garbage_collection, GCInfo}

GCInfo is a list which contains miscellaneous information about garbage collection for this process. The content of GCInfo may be changed without prior notice.

{group_leader, GroupLeader}

GroupLeader决定了最终的信息输出在什么地方,比如rpc会把远程执行的结果采集过来在当前shell显示,就用到这个GroupLeader

{heap_size, Size}

Size is the size in words of youngest heap generation of the process. This generation currently include the stack of the process. This information is highly implementation dependent, and may change if the implementation change.

{initial_call, {Module, Function, Arity}}

Module, Function, Arity is the initial function call with which the process was spawned.

{links, Pids}

关联进程列表

{last_calls, false|Calls}

The value is false if call saving is not active for the process (see process_flag/3). If call saving is active, a list is returned, in which the last element is the most recent called.

{memory, Size}

Size is the size in bytes of the process. This includes call stack, heap and internal structures.

{message_binary, BinInfo}

BinInfo is a list containing miscellaneous information about binaries currently being referred to by the message area. This InfoTuple is only valid on an emulator using the hybrid heap type. This InfoTuple may be changed or removed without prior notice.

{message_queue_len, MessageQueueLen}

这就是上文中我们特别关注的消息队列的长度

{messages, MessageQueue}

堆积消息列表

{min_heap_size, MinHeapSize}

最小堆大小

{min_bin_vheap_size, MinBinVHeapSize}

进程最小二进制虚拟堆大小

{monitored_by, Pids}

当前进程的监控进程列表

{priority, Level}

进程优先级,通过 process_flag(priority, Level).设置

{reductions, Number}

这个参数是进程负载的粗略评估指标.常备缩写为REds

Reduction is an approximate measure of how much CPU time they have used.

 

这个是一个计数,是Erlang虚拟机用来调度进程的一个依据,保证公平调度。
比如某个进程执行一个函数,reduction就会增加,执行gc reduction会进行减少。

 已回复 4月 13日 作者: litaocheng

{registered_name, Atom}

进程注册的名称.

{stack_size, Size}

进程栈大小(单位是word)

{status, Status}

进程状态,有exiting, garbage_collecting, waiting (for a message), running, runnable (ready to run, but another process is running), or suspended 

{total_heap_size, Size}

Size is the total size in words of all heap fragments of the process. This currently include the stack of the process.

{trap_exit, Boolean}

是否为系统进程,"Erlang系统进程"这样一个名字太容易产生误解了,本质上意味着什么呢?系统进程和普通进程唯一的区别就是可以把退出信号转换成为普通的消息.

 

 

 详情参见:http://www.erlang.org/doc/man/erlang.html#process_info-2

 

单个Erlang Process 占用多少内存?

An Erlang process is lightweight compared to operating systems threads and processes.

A newly spawned Erlang process uses 309 words of memory in the non-SMP emulator without HiPE support. (SMP support and HiPE support will both add to this size.) The size can be found out like this:

Erlang (BEAM) emulator version 5.6 [async-threads:0] [kernel-poll:false]

Eshell V5.6  (abort with ^G)
1> Fun = fun() -> receive after infinity -> ok end end.
#Fun<...>
2> {_,Bytes} = process_info(spawn(Fun), memory).
{memory,1232}
3> Bytes div erlang:system_info(wordsize).
309

The size includes 233 words for the heap area (which includes the stack). The garbage collector will increase the heap as needed.

 

Link:http://www.erlang.org/doc/efficiency_guide/processes.html

 

2014-2-28 10:58:48 重新做了上面的测试,看下结果:

 这个大小包含了233字的heap area(包含stack).GC会按照需求调整heap大小.
 
Eshell V5.10.2  (abort with ^G)
1> Fun=fun() -> receive after infinity ->ok end end.
#Fun<erl_eval.20.80484245>
2> {_,Bytes}=process_info(spawn(Fun),memory).
{memory,2656}
3> Bytes div erlang:system_info(wordsize).
332
4> 
4> erlang:system_info(wordsize).
8
5> process_info(spawn(Fun)).
[{current_function,{erlang,apply,2}},
{initial_call,{erlang,apply,2}},
{status,runnable},
{message_queue_len,0},
{messages,[]},
{links,[]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.26.0>},
{total_heap_size,233},
{heap_size,233},
{stack_size,0},
{reductions,0},
{garbage_collection,[{min_bin_vheap_size,46422},
                      {min_heap_size,233},
                      {fullsweep_after,65535},
                      {minor_gcs,0}]},
{suspending,[]}]
6> 
 

  

启动的时候通过+h 选项或者在spawn的时候通过spawn_opt指定min_heap_size可以定制进程创建的heap初始值;如果一个进程需要的heap较大,那么一开始分配好比通过GC增长开销要小,还有一个好处是如果进程heap有空余,GC可能会收缩heap,而minsize配置会防止这种情况.
 
除了Refc Bin外,进程间的消息数据都是通过拷贝实现的,消息如果是发送到其它节点,首先会被转换为Erlang External Format 然后再投递到TCP/IP Socket.接收到信息的节点解码消息并移交信息到指定进程.
 
Erlang的字面常量数据(literals)会在constant pools维护,每一个模块被加载之后都有自己的pool(换句话说这个设计的粒度是模块级).如果常量数据被发送到其它进程或者存入ETS,就会触发拷贝.这样做的原因是:运行时系统要能跟踪都所有常量数据的引用计数以便能够unload包含常量数据的模块,如果模块被unload,其包含的常量数据会被拷贝到使用它的进程的heap空间.
  
共享部分数据(sub_terms)有些情况下不会保留:1.发送到其它进程,2.用做进程创建初始化函数 3.存入ETS表;
这里有一篇论文专门讨论这个话题,有兴趣的可以看下:
 
On Preserving Term Sharing in the Erlang Virtual Machine
 
注意由于考虑到对现有应用的影响,Erlang开发团队并没有计划把Preserving of sharing 做成默认的.