Erlang服务器内存耗尽bug跟踪过程

标签: erlang 服务器 内存 | 发表时间:2011-10-25 21:44 | 作者:(author unknown) chuang
出处:http://erlangdisplay.iteye.com

本文描述朋友Erlang服务器内存耗尽bug的解决过程。
首先说明一下问题,服务器1千多人在线,16G内存快被吃光。玩家进程占用内存偏高:


接下来是解决过程。

第一步:
查看进程数目是否正常? erlang:system_info(process_count). 进程数目合理

第二步:
查看节点的内存消耗在什么地方?
> 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 输入.)
结果如下:
etop输出有点乱,超过一定范围变成了**,不过我们已经找到了内存占用最高的进程.

第四步:
查看占用内存最高的进程状态
> 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:erlc test.erl && erl -eval "test:t1()"
版本2:erlc test.erl && erl -eval "test:t2()"
你会看到版本1代码的调用堆栈在不断增长,内存也在增长, 而版本2函数调用地址保持不变,内存也没有发生变化!

总结:
1,服务器编程中,循环一定确保为尾递归
2,善于使用OTP,如果使用gen_server替换手写loop,就不会出现这个问题!


已有 3 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



相关 [erlang 服务器 内存] 推荐:

Erlang服务器内存耗尽bug跟踪过程

- chuang - Erlang Display - 不求完美,学以致用
本文描述朋友Erlang服务器内存耗尽bug的解决过程. 首先说明一下问题,服务器1千多人在线,16G内存快被吃光. 查看节点的内存消耗在什么地方?. 显示内存大部分消耗在进程上,由此确定是进程占用了大量内存. (以输出text方式启动etop,其间隔为1秒,输出行数为20行,按照内存排序. 这里spawn一个新进程,目的是输出etop数据时不影响erlang shell 输入.).

Erlang服务器内存吃紧的优化解决方法

- - CSDN博客系统运维推荐文章
问题提出:服务器100万人在线,16G内存快被吃光. 查看进程数目是否正常,是否超过了erlang虚拟机的最大进程数. 查看节点的内存瓶颈所在地方. 显示内存大部分消耗在进程上,由此确定是进程占用了大量内存. (以输出text方式启动etop,其间隔为1秒,输出行数为20行,按照内存排序. 这里spawn一个新进程,目的是输出etop数据时不影响erlang shell 输入.).

Erlang监测系统CPU、内存、磁盘

- chuang - Jobin的主页
Erlang的os_mon服务中提供了一些用于监测系统信息的服务. cpu_sup:监测CPU负载和使用率(Unix). disksup:监测磁盘(Unix、Windows). memsup:监测内存(Unix、Windows、VxWorks). os_sup:监测系统日志(Solaris、Windows).

高性能集群服务器(Erlang解决方案)

- BeerBubble - 淘宝核心系统团队博客
高性能集群服务器(Erlang解决方案) View more presentations from Feng Yu.

100万并发连接服务器笔记之Erlang完成1M并发连接目标

- - BlogJava-首页技术区
使用Erlang语言也写一个测试和前面大同小异的测试,在100万个并发连接用户情况下,就是想观察一下极显情况下的表现. 这个测试使用了优秀的Erlang界的明星框架. cowboy,加单易用的接口,避免了我们对HTTP栈再次进行闭门造车. 运行在VMWare Workstation 9中,64位Centos 6.4系统,分配14.9G内存左右,双核4个线程,服务器安装Erlang/OTP R16B,最新版本支持异步代码热加载,很赞.

sysbench测试MySQL服务器性能(cpu,io,内存,mysql等)

- - CSDN博客数据库推荐文章
Sysbench的安装请参考http://blog.csdn.net/mchdba/article/details/8951289. sysbench采用寻找最大素数的方式来测试CPU的性能. 首先生成需要的测试文件,文件总大小1000M,16个并发线程,随机读写模式. 执行完后会在当前目录下生成一堆小文件.

线上服务器内存使用量已达到90%报警(内存泄漏)

- - Linux - 操作系统 - ITeye博客
2016-05-06中午11:56,收到“[sentry2]2016-05-06 11:56:09. xxxxxxhost xxx.xxx.xxx.xxx 内存使用已达到90.18%”报警. 首先在脑海浮现的, 应该哪里出现内存泄漏了. Sentry 监控系统查看了该服务的“ 服务器监控”指标,发现其中2台机器的 内存使用量都超过了90%,另外2台尽然没有监控数据(以前是有的).

《Erlang编程指南》读后感

- David Ruan - Tim[后端技术]
在云时代,我们需要有更好的能利用多核功能及分布式能力的编程语言,Erlang在这方面具有天生的优势,因此我们始终对它保持强烈关注. 按:此为客座文章,投稿人为新浪微博基础研发工程师赵鹏城(http://weibo.com/iamzpc),以下为原文. 在对一个分布式KV存储系统的研究过程中,我有幸遇到了Erlang语言.

Erlang十分钟快速入门

- - 水煮沉浮
Erlang概述Erlang不但是一种编程语言,而且它具有比编程语言更加贴近操作系统的一些特性:并发线程、作业调度、内存管理、分布式、网络化等. 据说使用Erlang编写的Yaws Web服务器,其并发性能是apache的15倍. 这个Erlang初始开源版本包含了Erlang的实现,同时它也是用于构建分布式高可用性系统的Ericsson中间件的最大组成部分.

whatsapp深度使用Erlang有感

- - 系统技术非业余研究
原创文章,转载请注明: 转载自 系统技术非业余研究. whatsapp深度使用Erlang有感. 这么多年过去了,社区还在讨论erlang是不是小众语言,各种怀疑的时候,whatsapp已经把erlang用到了极致. 更为搞笑的是 主要开发者Rick Reed([email protected]),之前在Yahoo!, SGI工作,有着深厚的系统性能的背景.