Erlang 内存泄漏分析

乘机项目越来越着重Erlang,遭逢的题材也跟着增多。前段时间线上系统境遇内存高消耗难题,记录一下troubleshooting的解析进程。线上系统用的是Erlang
Odyssey16B02版本。

难点讲述

有几台线上系统,运行一段时间,内存飙升。系统模型很简短,有互联网连接,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消耗的内存等等。

Erlang process创造数量

线上系统发现重大内存消耗都在process上边,接下去要分析,是process内存泄漏了,仍然process成立数量太多导致。

> erlang:system_info(process_limit).  %%查看系统最多能创建多少process
> erlang:system_info(process_count).  %%当前系统创建了多少process

system_info()回到当前系统的片段音信,比如系统process,port的多少。执行上边命令,大吃一惊,唯有2,3k的互连网连接,结果Erlang
process已经有10多w了。系统process创造了,不过因为代码恐怕其他原因,堆积没有自由。

查阅单个process的音信

既然如此是因为process因为某种原因堆积了,只好从process里找原因了

先要获取堆积process的pid

> i().  %%返回system信息
> i(0,61,886).  %% (0,61,886)是pid

观察有不少process
hang在那边,查看具体pid消息,发现message_queue有几条新闻尚未被拍卖。上面就用到强大的erlang:process_info()方法,它可以得到进度卓绝丰盛的新闻。

> erlang:process_info(pid(0,61,886), current_stacktrace).
> rp(erlang:process_info(pid(0,61,886), backtrace)).

翻看进度的backtrace时,发现上面的消息

0x00007fbd6f18dbf8 Return addr 0x00007fbff201aa00 (gen_event:rpc/2 + 96)
y(0)     #Ref<0.0.2014.142287>
y(1)     infinity
y(2)     {sync_notify,{log,{lager_msg,[], ..........}}
y(3)     <0.61.886>
y(4)     <0.89.0>
y(5)     []

process在拍卖Erlang第三方的日志库lager时,hang住了。

难题原因

查看lager的文档,发现以下音信

Prior to lager 2.0, the gen_event at the core of lager operated
purely in synchronous mode. Asynchronous mode is faster, but has no
protection against message queue overload. In lager 2.0, the
gen_event takes a hybrid approach. it polls its own mailbox size and
toggles the messaging between synchronous and asynchronous depending
on mailbox size.

{async_threshold, 20}, {async_threshold_window, 5}

This will use async messaging until the mailbox exceeds 20 messages,
at which point synchronous messaging will be used, and switch back to
asynchronous, when size reduces to 20 – 5 = 15.

If you wish to disable this behaviour, simply set it to ‘undefined’.
It defaults to a low number to prevent the mailbox growing rapidly
beyond the limit and causing problems. In general, lager should
process messages as fast as they come in, so getting 20 behind should
be relatively exceptional anyway.

本来lager有个布局项,配置message未处理的数目,假使message堆积数超出,则会用 同步 格局处理!

当前系统打开了debug log,洪涝般的log把系统给冲垮了。

鬼子也碰到类似难题,这么些thread给我们的解析带来诸多相助,谢谢一下。

总结

Erlang提供了丰裕的工具,可以在线进入系统,现场分析难点,这么些这些有助于快捷、神速的定位难点。同时,强大的Erlang
OTP让系统有更平稳的保证。大家还会继续挖掘Erlang,期待有更加多的举行分享。

 

关于小编

今日头条@liaolinbo,云巴上位工程师。曾于Oracle工作。

相关文章