用 recon 定位 Erlang 内存泄露问题

前面一部分直接从以前的 blog copy 过来。http://blog.yunba.io/erlang-memory-leak/

随着项目越来越依赖Erlang,碰到的问题也随之增加。前段时间线上系统碰到内存高消耗问题,记录一下troubleshooting的分析过程。线上系统用的是Erlang R16B02版本。

问题描述

有几台线上系统,运行一段时间,内存飙升。系统模型很简单,等待网络新连接,pool中找新可用process进行处理。top命令观察,发现内存都被Erlang进程给吃完了,netstat命令查看网络连接数,才区区几K。问题应该是Erlang内存泄漏了。

分析方法

Erlang系统有个好处,可以直接进入线上系统,在生产现场分析问题。我们系统是通过Rebar管理的,可以用不同方法进入线上系统。

本机登录

可以直接登录到线上机器,然后通过以下命令attach到Erlang系统里面

$ cd /path/to/project
$ rel/xxx/bin/xxx attach
(node@host)> 

通过remote shell

获取Erlang系统的cookie

$ ps -ef |grep beam  %%找到参数 --setcookie

新开一个shell,使用同样的cookie,不同的nodename

$ erl --setcookie cookiename -name test@127.0.0.1

用start remote shell进入系统

Erlang R16B02 (erts-5.10.3) [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.3  (abort with ^G)  
(test1@127.0.0.1)1> net_adm:ping('node@127.0.0.1').
pong  
(test1@127.0.0.1)2> nodes().
['node@127.0.0.1']
(test1@127.0.0.1)3> 
User switch command  
 --> h
  c [nn]            - connect to job
  i [nn]            - interrupt job
  k [nn]            - kill job
  j                 - list all jobs
  s [shell]         - start local shell
  r [node [shell]]  - start remote shell
  q                 - quit erlang
  ? | h             - this message
 --> r 'node@127.0.0.1'
 --> j
   1  {shell,start,[init]}
   2* {'node@127.0.0.1',shell,start,[]}
 --> c 2

分析流程

Erlang有很多工具,可以分析系统信息,比如appmonwebtool。但是系统内存严重不足,已经没有办法启动这些工具了,幸好还有Erlang shell。

Erlang shell自带了很多有用的命令,可以用help()方法查看

> help().

Erlang系统内存消耗情况

top结果显示,是内存问题,所以第一步可以先看看Erlang的系统内存消耗情况

> erlang:memory().

memory()可以看到Erlang emulator分配的内存,有总的内存,atom消耗的内存,process消耗的内存等等。

用 recon 工具

copy recon beam

$ git clone https://github.com/ferd/recon.git
$ cd recon/
$ ./rebar compile
# 根据实际情况,使用项目发布的 ebin 路径
$ cp ebin/*.beam /home/yunba/elockaccount/rel/eLockAccount/lib/eLockAccount-1/ebin/

recon 命令

加载 recon

> l(recon).
{module,recon}
> l(recon_lib).
{module,recon_lib}
> l(recon_alloc).
{module,recon_alloc}

查看使用内存最多的 10 个进程

> recon:proc_count(memory, 10).
[{<0.1470.0>,9571192,
  [eLockAccount_monthly_credit_update_server,
   {current_function,{gen_server,loop,6}},
   {initial_call,{proc_lib,init_p,5}}]},
 {<0.1114.0>,8238352,
  [code_server,
   {current_function,{code_server,loop,1}},
   {initial_call,{erlang,apply,2}}]},
 {<0.8173.1>,4720632,
  [{current_function,{gen_server,loop,6}},
   {initial_call,{proc_lib,init_p,5}}]},
 {<0.1113.0>,1577592,
  [kernel_sup,
   {current_function,{gen_server,loop,6}},
   {initial_call,{proc_lib,init_p,5}}]},
 {<0.1167.0>,426816,
  [lager_event,
   {current_function,{gen_event,fetch_msg,5}},
   {initial_call,{proc_lib,init_p,5}}]},
 {<0.1108.0>,372464,
  [error_logger,
   {current_function,{gen_event,fetch_msg,5}},
   {initial_call,{proc_lib,init_p,5}}]},
 {<0.1136.0>,318208,
  [{current_function,{shell,get_command1,5}},
   {initial_call,{erlang,apply,2}}]},
 {<0.1109.0>,285120,
  [application_controller,
   {current_function,{gen_server,loop,6}},
   {initial_call,{erlang,apply,2}}]},
 {<0.16674.2>,264096,
  [{current_function,{shell,shell_rep,4}},
   {initial_call,{erlang,apply,2}}]},
 {<0.1147.0>,196976,
  [{current_function,{file_io_server,server_loop,1}},
   {initial_call,{erlang,apply,2}}]}]

总结

Erlang提供了丰富的工具,可以在线进入系统,现场分析问题,这个非常有助于高效、快速的定位问题。同时,强大的Erlang OTP让系统有更稳定的保证。我们还会继续挖掘Erlang,期待有更多的实践分享。