Home > Erlang探索 > hibernate使用注意事项

hibernate使用注意事项

October 2nd, 2011

原创文章,转载请注明: 转载自系统技术非业余研究

本文链接地址: hibernate使用注意事项

hibernate的作用是在进程闲的时候或者内存紧张的时候,通过重新整理进程的堆和栈内存来减少内存的消耗,同时维持进程之前的状态,但是误用会引起些问题,这里我们来展开下。

erlang:hibernate文档参考这里

erlang:hibernate(Module, Function, Args)

Types:

Module = Function = atom()
Args = [term()]
Puts the calling process into a wait state where its memory allocation has been reduced as much as possible, which is useful if the process does not expect to receive any messages in the near future.

The process will be awaken when a message is sent to it, and control will resume in Module:Function with the arguments given by Args with the call stack emptied, meaning that the process will terminate when that function returns. Thus erlang:hibernate/3 will never return to its caller.

If the process has any message in its message queue, the process will be awaken immediately in the same way as described above.

In more technical terms, what erlang:hibernate/3 does is the following. It discards the call stack for the process. Then it garbage collects the process. After the garbage collection, all live data is in one continuous heap. The heap is then shrunken to the exact same size as the live data which it holds (even if that size is less than the minimum heap size for the process).

If the size of the live data in the process is less than the minimum heap size, the first garbage collection occurring after the process has been awaken will ensure that the heap size is changed to a size not smaller than the minimum heap size.

Note that emptying the call stack means that any surrounding catch is removed and has to be re-inserted after hibernation. One effect of this is that processes started using proc_lib (also indirectly, such as gen_server processes), should use proc_lib:hibernate/3 instead to ensure that the exception handler continues to work when the process wakes up.

请注意最后最后一句话, 由于hibernate把堆栈全部清掉了,在wakeup的时候,如果之前有try catch需要重新构建, 这就是proc_lib考虑周到的地方。
首先我们先看下try catch堆栈的情况,看例子:

$ cat try_stack.erl
-module(try_stack).
-compile(export_all).

loop()->
    receive _-> loop() end.
	
x()->
    register(x, self()),
    loop().

y()->
    register(y, self()),
    try 
	loop()
    catch
	_:_ ->ok
    end.

start()->
    spawn(fun x/0),
    spawn(fun y/0),
    ok.
    
$ erlc try_stack.erl
$ erl -s try_stack
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> 
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution
A

按下大写的A, 产生crashdump文件,用CrashDumpViewer看到进程的堆栈情况,可以看到下图:

从图中我们知道,try catch 是往堆栈里面都压入了出错时候的返回地址,而普通的函数没有压入任何东西。

确认了这个问题,就很容易理解proc_lib:hibernate的实现了,我们看代码:

%% proc_lib.erl:L192

hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
    erlang:hibernate(?MODULE, wake_up, [M, F, A]).

wake_up(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
    try
       	apply(M, F, A)
    catch
        Class:Reason ->
            exit_p(Class, Reason)
    end.

总结:标准库的东西总是会考虑比较周到,我们尽量多用gen_server,proc_lib这样的基础设施。
祝玩得开心!

Post Footer automatically generated by wp-posturl plugin for wordpress.

Categories: Erlang探索 Tags: ,
  1. hongzhu2012
    March 14th, 2014 at 10:40 | #1

    昨天用了下proc_lib:hibernate 还是能省一部分内存的,谢谢峰哥的分享哈~~

    Yu Feng Reply:

    一起进步。

Comments are closed.