ERlang 堆溢出
Posted
技术标签:
【中文标题】ERlang 堆溢出【英文标题】:ERlang HEAP overflow 【发布时间】:2011-05-12 02:15:13 【问题描述】:我有一个程序..
差不多完成了..
但是我在运行大约 12 小时后看到堆崩溃。
我记得听说你不能以某种方式对 erlang 进行编程,如果不是在你递归堆栈建立时。谁能举个例子吗?
还有什么方法可以实时监控正在堆积的进程?
问候
编辑 - 怎么样
loop() ->
receive
sys, Msg ->
handle_sys_msg(Msg),
loop();
From, Msg ->
Reply = handle_msg(Msg),
From ! Reply,
loop();
_ -> continue
end,
loop().
【问题讨论】:
您的编辑是尾递归的,这意味着它不会在堆栈上构建任何东西。 正如@TERRIBLE ADVICE 非常正确地指出您的编辑不是尾递归 【参考方案1】:即使你的编辑不是尾递归:
loop() ->
receive
sys, Msg ->
handle_sys_msg(Msg),
loop();
From, Msg ->
Reply = handle_msg(Msg),
From ! Reply,
loop();
_ -> continue
end,
loop().
一个函数的执行顺序是:receive ... end, loop()
。现在,如果您收到sys, _
消息,将从接收中调用loop/0
,将上面的执行顺序转换为等效于:
loop() ->
receive
loop() ->
receive
...
end,
loop(),
end,
loop() ->
...
问题在于,如果您从接收中调用loop()
,VM 仍然必须存储返回点才能在receive
之后运行loop()
。
要使函数尾递归,您需要执行以下任一操作:
loop() ->
receive
sys, Msg ->
handle_sys_msg(Msg);
From, Msg ->
Reply = handle_msg(Msg),
From ! Reply;
_ -> continue
end,
loop().
或
loop() ->
receive
sys, Msg ->
handle_sys_msg(Msg),
loop();
From, Msg ->
Reply = handle_msg(Msg),
From ! Reply,
loop();
_ -> loop()
end.
调用loop()
确实是总是函数中要做的最后一件事。
【讨论】:
如果接收在 try-catch 的“try”段中怎么办?如果出现异常,VM 是否仍会存储返回点? (这没有意义,因为异常会在嵌套的“try”中捕获 是的。在try ... catch
的情况下,如果表达式发生在两者之间,VM 将存储一个堆栈帧而不是尾递归。在这种情况下使用:try risky_thing() of Res -> recursive_call() catch ... end
这将防止此问题【参考方案2】:
这样的东西可以用来监控系统中进程的当前堆使用情况。只需将它放在循环 gen_server 中的打印输出中,或者每隔一段时间在 shell 中运行它。
lists:reverse(lists:keysort(2,
[Pid,catch element(2,process_info(Pid,total_heap_size)) || Pid <- processes()])).
【讨论】:
【参考方案3】:进程循环必须是尾递归的。
不要
loop() ->
receive
sys, Msg ->
handle_sys_msg(Msg),
loop();
From, Msg ->
Reply = handle_msg(Msg),
From ! Reply,
loop()
end,
io:format("Message is processed~n", []).
见:http://www.erlang.org/doc/efficiency_guide/processes.html
【讨论】:
以上是关于ERlang 堆溢出的主要内容,如果未能解决你的问题,请参考以下文章
JVM内存溢出分析:堆内存溢出+虚拟机+方法区——JVM系列
Java JVM:内存溢出(栈溢出,堆溢出,持久代溢出以及 nable to create native thread)