- 浏览: 333111 次
- 性别:
- 来自: 北京
最新评论
-
perfect_control:
真的很详细,一些东西很容易被我忽略掉了
使用fprof进行性能分析 -
leeyisoft:
http://www.erlangqa.com/ 怎么变成 “ ...
Erlang问答网站,欢迎各位提出问题,解答问题。 -
simsunny22:
4年之后我才看到 慢慢的干货
Erlang服务器内存耗尽bug跟踪过程 -
爱死我:
...
使用etop查看系统中进程信息 -
宋兵甲:
在跑这个服务的时候,每秒建立一个客户端连接,连续建立10000 ...
自己写一个tcp 通用服务器
本文描述朋友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,就不会出现这个问题!
首先说明一下问题,服务器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,就不会出现这个问题!
评论
9 楼
simsunny22
2015-11-06
4年之后我才看到 慢慢的干货
8 楼
yjl49
2012-03-14
顺便问个问题编译后的字节码咋查看?谢谢
7 楼
yjl49
2012-03-14
litaocheng 写道
chaoslawful 写道
最好说明一下,try...catch结构中只有try block ....
顶!谢谢指点哦~~~~
文章看了几遍了,觉得原始的问题还没有解决:
以下这段代码的问题出在哪里?
send_msg(Socket, Pid) -> try receive {send, Bin} -> ... {inet_reply, _Sock, Result} -> ... catch _:_ -> send_msg(Sock, Pid) end.
按chaoslawful的说法
send_msg(Socket, Pid) -> try receive ... ... end catch _:_ -> send_msg(Sock, Pid) end.
这种结构的代码是没问题的(只要recive ...end 中间没有再调用send_msg/2)
6 楼
zdx3578
2012-02-29
good 学习力
5 楼
expp
2011-11-04
记得armstrong爷爷某书中提高erlang做法是任其崩溃,ms不提倡try catch防御式的做法
4 楼
francis333
2011-10-31
good! 帅哥,我接触erlang没多久,发现确实有其他语言不能比拟的优势,能不能写一篇关于进程字典的文章,包括其用法等等,我目前在开发阶段遇到一个问题,我需要从数据库读取信息,然后逐个执行模块,但是当数据库信息更新后,需要在不停止已运行模块的前提下更新该模块执行的个别参数,从网上看好像可以通过进程字典实现参数的替换,不知道行不行
3 楼
litaocheng
2011-10-26
chaoslawful 写道
最好说明一下,try...catch结构中只有try block ....
顶!谢谢指点哦~~~~
2 楼
chaoslawful
2011-10-26
最好说明一下,try...catch结构中只有try block(即try/catch之间的代码)中的函数调用无法构造成尾递归,而catch block(即catch/end之间的代码)中的函数调用则可构造为尾递归形式。故文中代码里
有问题的非尾递归调用存在于try/catch间receive中被隐去的部分里,而catch/end之间的那个 send_msg/2 调用则确实是尾递归,不会造成问题。同理,后面测试用例中造成内存占用不断上涨的问题在于 do_t1/0 中 try/catch 间 receive 中的 do_t1/0 调用,而非 catch/end 间的 do_t1/0 调用。
对于有疑问的 Erlang 代码,从 erlc 产生的字节码文件其实可以看得更清楚,尾递归调用会使用 call_last 字节码,而普通调用则使用 call 字节码,一目了然。比如如下代码
生成字节码文件:
send_msg(Socket, Pid) -> try receive {send, Bin} -> ... {inet_reply, _Sock, Result} -> ... catch _:_ -> [color=red]send_msg(Sock, Pid)[/color] end.
有问题的非尾递归调用存在于try/catch间receive中被隐去的部分里,而catch/end之间的那个 send_msg/2 调用则确实是尾递归,不会造成问题。同理,后面测试用例中造成内存占用不断上涨的问题在于 do_t1/0 中 try/catch 间 receive 中的 do_t1/0 调用,而非 catch/end 间的 do_t1/0 调用。
对于有疑问的 Erlang 代码,从 erlc 产生的字节码文件其实可以看得更清楚,尾递归调用会使用 call_last 字节码,而普通调用则使用 call 字节码,一目了然。比如如下代码
-module(x). -compile(export_all). x() -> try x() catch _:_ -> x() end.
生成字节码文件:
$ erlc +"'S'" x.erl $ cat x.S ... {function, x, 0, 2}. {label,1}. {func_info,{atom,x},{atom,x},0}. {label,2}. {allocate_zero,1,0}. {'try',{y,0},{f,3}}. [color=red]{call,0,{f,2}}.[/color] {try_end,{y,0}}. {deallocate,1}. return. {label,3}. {try_case,{y,0}}. [color=green]{call_last,0,{f,2},1}.[/color] ...
1 楼
orez88
2011-10-26
好文章,学习了
发表评论
-
Erlang问答网站,欢迎各位提出问题,解答问题。
2012-03-18 15:07 5228平时收到很多关于Erlang的问题,我都尽量一一解答,可是时间 ... -
Emakefile并行编译
2011-11-17 13:15 7597项目代码越来越多,使用erlang编译也越来越慢。无论是Mak ... -
inet:getstat/2小用法
2011-04-27 09:32 4546inet:getstat/2的用处 在 ... -
Erlang游戏开发-协议
2011-04-22 16:10 10673Erlang游戏开发-协议 ... -
Gearman Erlang Client
2010-10-17 21:14 3677Gearman Gearman是一个通用的任务调度框架。 ... -
ECUG归来
2010-10-17 21:02 2936今天ECUG V圆满结束了,不知不觉作为讲师已经参加过3次大会 ... -
gen-erl-app快速生成erlang app 框架
2010-04-07 14:22 3949经常需要创建各种erlang app,这个过程一旦掌握,就很繁 ... -
erl-redis发布
2010-03-30 11:44 5759最近几天因为需要,实现了一个redis erlang clie ... -
用Erlang做了很多事
2010-01-19 14:08 5036因为工作及时间关系,最近比较忙碌,没有太多的时间写文章。 ... -
ecug topic - erlang开发实践
2009-11-11 10:04 3713从ecug归来,感觉不错,大家学习探讨的积极性很高哦。 很高 ... -
reltool用户指南
2009-11-02 22:27 6292说明,最近比较忙,没有太多时间更新blog,请各位朋友谅解. ... -
Erlang定时任务server (仿crontab语法)
2009-09-23 18:03 6288好久不写blog了,看到yufeng老大那么活跃,我也“耐不住 ... -
Erlang进程之错?
2009-07-27 15:06 3652前阵子erlang-china关于erla ... -
CNode指南
2009-07-27 14:13 3285好久不发文章,因为工作太忙。这个东西就凑凑数吧。各位见谅。 ... -
Erlang类型及函数声明规格
2009-06-08 22:41 9476Erlang类型及函数声明 ... -
使用etop查看系统中进程信息
2009-05-29 13:57 6094Erlang提供了丰富的开发工具,你认为没有的时候,很可能是你 ... -
创建gen_server组解决单process瓶颈
2009-05-27 17:05 5207并发和顺序是一个令人 ... -
又有人投入Erlang的怀抱了:37Signals Campfire loves Erlang
2009-05-14 23:00 3577就喜欢看这样的东西... This is so juicy ... -
list random shuffle实现
2009-05-07 13:41 4318在项目中需要对list进行随机shuffle,但是在erlan ... -
Erlang开发建议(杂记版)
2009-04-24 18:27 6443以下是在erlang项目开发中的一些记录,即包含很多通俗易懂的 ...
相关推荐
搭建自己的erlang服务器(基于RabbitMQ改进)(一)
看到另外两个分好高,我把另外两个二合一啦。远古封神+英雄远征的ERLANG游戏服务器代码,看过了,感觉不错的。
Erlang核心开发者Lukas Larsson在2014年3月份Erlang Factory上的一个演讲详细介绍了Erlang内存体系的原理以及调优案例 根据siyao zheng博客上听写的资源进行的翻译,大致只翻译了80%但核心部分已经完整,希望对大家...
erlang单服游戏服务器,mnesia数据库,小型的游戏demo,编译可以启动,可以用来学习erlang游戏服务器相关的东西
java php python erlang 千万级内存数据性能比较,
主要介绍了Erlang项目内存泄漏分析方法,本文讲解了分析方法、分析流程并找到问题原因和解决方法,需要的朋友可以参考下
erlang整理的一些心得和lunix查看cpu和内存信息的方法
erlang内存管理
各种版本的openpoker集合,英雄远征服务器代码,研究erlang的好代码
erlang具有良好的高并发性 支持热更新 本代码适合初学者更快的上手
erlang_ls 一个实现微软语言服务器协议 3.15 的 Erlang 服务器。最低要求 快速开始编译项目: make要在/usr/local/bin安装生成的erlang_ls escript: make install命令行参数这些是可以提供给erlang_ls脚本的命令行...
Erlang MQTT消息服务器简称EMQTT。EMQTT是采用Erlang语言开发,全面支持MQTT V3.1.1协议,支持集群和大规模连接的开源MQTT消息服务器。EMQTT致力于发布一个基于Erlang/OTP语言平台,企业级稳定可靠,完全开源免费,...
erlang游戏服务器网关源码.zip
高性能集群服务器Erlang解决方案
erlang入门电子书 erlang编程 Introducing Erlang,作者Simon.St.Laurent
erlang 安装包
openpoker源码 erlang写的网游服务器源码 OpenPoker是一个大型多人扑克网游,内建支持了容错能力,负载平衡和无限制的规模大小。
erlang写的一个特别的web服务器 .转载 谢谢
erlang实现的网游服务器(OpenPoker系统)erlang实现的网游服务器(OpenPoker系统)erlang实现的网游服务器(OpenPoker系统)
Erlang及其应用Erlang及其应用Erlang及其应用