hibernate 主要用于在内存空闲时,通过整理进程的stack,回收进程的heap 来达到回收内存节省资源的效果.

hibernate 可用于OTP 进程以及普通进程, hibernate 的官方文档 erlang:hibernate/3

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 Argswith 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.

1, 主要用于某进程可能会在未来短期内空闲时

2, hibernate 会清空进程的stack 然后进程GC

从Erlang 进程PCB(process control block) 的角度来看, heap 主要用来存储复杂数据结构(如:tuples, lists, big integers), 而stack 主要存储简单的数据结构和指向在heap 中的复杂数据结构的引用. hibernate 清空进程stack 之后再进行进程GC ,会尽可能的保证heap 中的资源回收. 相比普通的GC 更彻底.

gen_server hibernate

gen_server module 对hibernate 调用的支持主要体现在 callback 返回参数(如:{reply, R, S, hibernate}) 以及enter_loop 函数.

 loop(Parent, Name, State, Mod, hibernate, Debug) ->
proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug]); wake_hib(Parent, Name, State, Mod, Debug) ->
Msg = receive
Input ->
Input
end,
decode_msg(Msg, Parent, Name, State, Mod, hibernate, Debug, true).

如果loop/6 函数的Timeout 参数值为 'hibernate', gen_server process 就会调用proc_lib:hibernate/3 函数使进程进入hibernate 状态. 而当有消息被发送至本进程时, 进程就会被resume并调用wake_hib/5 回到正常的decode_msg 流程.

而enter_loop 函数的作用是 make 一个已经存在的process 变成gen_server 进程:

The calling process will enter the gen_server receive loop and become a gen_server process.

各界对hibernate 的使用

进程hibernate 会清空进程的stack 并进行GC ,并且进程被resume 时,需要为进程重新分配heap 资源. hibernate 能够为虚拟机节省更多的内存资源, 但是也会加重CPU的负担. 那社区里对这一特性是怎么使用的?

Mochiweb

在大名鼎鼎的 A Million-user Comet Application with Mochiweb 中, 作者提到了使用hibernate 作为优化手段之一.

This sounds reasonable - let's try hibernating after every message and see what happens.

并且起到了立竿见影的效果:

Judicious use of hibernate means the mochiweb application memory levels out at 78MB Resident with 10k connections, much better than the 450MB we saw in Part 1. There was no significant increase in CPU usage.

是的,Mochiweb 的使用方式, 是在接收到message 之后立即使用hibernate.

Ejabberd

Ejabberd 对hibernate 的使用比较谨慎, 只有在进程未收到任何信息一段时间后, 才使用hibernate .

ejabberd_receiver module 是 Ejabberd 框架中的socket message 接收module, ejabberd_receiver 是gen_server 进程,衔接socket 和C2S gen_fsm 进程. ejabberd_receiver 设置了一个超时时间, 超时时间被触发(也就是进程收到timeout)后, ejabberd_receiver 进程会调用proc_lib:hibernate/3 .而当有新的消息sent 到该进程之后, hibernate 会使用 gen_server:enter_loop/3 函数, 唤醒ejabberd_receiver 进程并重新进入gen_server process .

可以看出 Ejabberd 对 hibernate 的使用,比较谨慎,只有当进程在一段时间内未收到消息(也就是一段时间内空闲),才会使用hibernate .

 handle_info(timeout, State) ->
proc_lib:hibernate(gen_server, enter_loop,
[?MODULE, [], State]),

RabbitMQ

RabbitMQ 同样使用了hibernate, 是在gen_server2 代码中,同样是使用了Ejabberd 类似的方式(进程一段时间空闲后, 才使用hibernate).

使用了 gen_server2 behavior module 的init 函数:

 init(Q) ->
process_flag(trap_exit, true),
?store_proc_name(Q#amqqueue.name),
{ok, init_state(Q#amqqueue{pid = self()}), hibernate,
{backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE},
?MODULE}.

L5 定义了hibernate 相关的参数.

而在gen_server2 module 中, 回调user module init 的方法:

         {ok, State, Timeout, Backoff = {backoff, _, _, _}, Mod1} ->
Backoff1 = extend_backoff(Backoff),
proc_lib:init_ack(Starter, {ok, self()}),
loop(GS2State #gs2_state { mod = Mod1,
state = State,
time = Timeout,
timeout_state = Backoff1 });

会将backoff hibernate 相关参数与gs2_state 一同作为 MAIN loop 的参数.紧接着,gen_server2 loop/1 函数进入 process_next_msg/1 :

 process_next_msg(GS2State = #gs2_state { time          = Time,
timeout_state = TimeoutState,
queue = Queue }) ->
case priority_queue:out(Queue) of
{{value, Msg}, Queue1} ->
process_msg(Msg, GS2State #gs2_state { queue = Queue1 });
{empty, Queue1} ->
{Time1, HibOnTimeout}
= case {Time, TimeoutState} of
{hibernate, {backoff, Current, _Min, _Desired, _RSt}} ->
{Current, true};
{hibernate, _} ->
%% wake_hib/7 will set Time to hibernate. If
%% we were woken and didn't receive a msg
%% then we will get here and need a sensible
%% value for Time1, otherwise we crash.
%% R13B1 always waits infinitely when waking
%% from hibernation, so that's what we do
%% here too.
{infinity, false};
_ -> {Time, false}
end,
receive
Input ->
%% Time could be 'hibernate' here, so *don't* call loop
process_next_msg(
drain(in(Input, GS2State #gs2_state { queue = Queue1 })))
after Time1 ->
case HibOnTimeout of
true ->
pre_hibernate(
GS2State #gs2_state { queue = Queue1 });
false ->
process_msg(timeout,
GS2State #gs2_state { queue = Queue1 })
end
end
end.

在priority_queue 队列为空(L7)的情况下, 等待message(L23) 并设置 ?HIBERNATE_AFTER_MIN (L11, L28)超时, 超时触发之后, 首先回调 user module 的handle_pre_hibernate callback 方法(gen_server2 特有), 最后调用hibernate .

可以看出RabbitMQ 使用hibernate 的方式更为谨慎.

参考文献:

1, http://blog.yufeng.info/archives/1615

2, http://www.erlang.org/doc/man/erlang.html#hibernate-3

3, http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-2

4, Characterizing the Scalability of Erlang VM

Erlang generic standard behaviours -- gen_server hibernate的更多相关文章

  1. Erlang generic standard behaviours -- gen_server system msg

    这是Erlang generic standard behaviors gen_server 分析的系列的最后一篇,主要分析gen_server module 辅助性的功能函数. 在gen_serve ...

  2. Erlang generic standard behaviours -- gen_server module

    在分析完gen module (http://www.cnblogs.com/--00/p/4271386.html)之后,就可以开始进入gen_server 的主体module 了.gen_serv ...

  3. Erlang generic standard behaviours -- gen_server noblock call

    在Erlang 系统中,经常需要gen_server 进程来处理共享性的数据,也就是总希望一个gen_server 进程来为多个普通进程提供某种通用性的服务,这也是gen_server 设计的初衷.但 ...

  4. Erlang generic standard behaviours -- gen_server terminate

    gen_server 主体 module 已经分析完了(http://www.cnblogs.com/--00/p/4271982.html),接着,分析下gen_server 中的terminate ...

  5. Erlang generic standard behaviours -- gen

    在分析 gen_server (或者是gen_fsm )之前,首先应该弄明白,gen 这个module . -module(gen). -compile({inline,[get_node/1]}). ...

  6. Erlang generic standard behaviours -- summary

    gen_server 相关的片段分析得也差不多了, 这篇作为一个简要的总结.这一系列相关的分析暂且告一段落(之后如有必要,还会回来的 ^^ ),下一个系列主要是以pool 相关, 包括但不仅限于开源项 ...

  7. erlang四大behaviour之一gen_server

      来源:http://www.cnblogs.com/puputu/articles/1701017.html erlang程序设计里面有个设计原则就是把你的进程构造成树,把共用代码提出来,特定功能 ...

  8. erlang四大behaviour之一gen_server(转载)

    erlang程序设计里面有个设计原则就是把你的进程构造成树,把共用代码提出来,特定功能用自己的module实现,这也就是behaviour了,应用behaviour可以减少与本身事务无关的代码量,设计 ...

  9. gen_server的模板

    -module(first_gen_server).-behaviour(gen_server).-export([init/1, handle_call/3, handle_cast/2, hand ...

随机推荐

  1. Python中实现switchcase

    # 第一种方式使用python中的字典# author:wanstack def first_func(): print('first_func') def second_func(): print( ...

  2. TLS就是SSL的升级版+网络安全——一图看懂HTTPS建立过程——本质上就是引入第三方监管,web服务器需要先生成公钥和私钥,去CA申请,https通信时候浏览器会去CA校验CA证书的有效性

    起初是因为HTTP在传输数据时使用的是明文(虽然说POST提交的数据时放在报体里看不到的,但是还是可以通过抓包工具窃取到)是不安全的,为了解决这一隐患网景公司推出了SSL安全套接字协议层,SSL是基于 ...

  3. vue项目使用hbuilder打包后,真机测试白屏

    在命令行直接运行 npm run build后,生成的dist文件中,直接打开index.html文件 Tip: built files are meant to be served over an ...

  4. jquery下跨域请求之代码示例

    场景描述: 在域A下异步获取B域下的接口: 实现方法: $.ajax({ url : (Q.lottery.serverTimeUrl || 'about:blank'), error : funct ...

  5. UML_04_时序图

    一.前言 时序图建模工具,推荐一个工具 https://www.zenuml.com/ 时序图是一种强调消息时序的交互图,他由对象(Object).消息(Message).生命线(Lifeline) ...

  6. iOS笔记之常用工具

    CocoaPods: 类库管理工具,使用教程见http://www.devtang.com/blog/2014/05/25/use-cocoapod-to-manage-ios-lib-depende ...

  7. C++STL内存配置的设计思想与关键源码分析

    说明:我认为要读懂STL中allocator部分的源码,并汲取它的思想,至少以下几点知识你要了解:operator new和operator delete.handler函数以及一点模板知识.否则,下 ...

  8. 初次使用VCS仿真软件

    由于刚开始接触VCS,对于VCS不是太了解,在网上找了很多的资料终于遇到了一个相对比较初级的入门资料,这个资料是以一个简单的4位加法器为例来介绍vcs的用法的,比较好入门,这个文章的地址如下: htt ...

  9. iOS系统知识架构(转)

    转载的,哪些所谓的资深开发,谁敢说自己没有知识盲区?http://ios.skyfox.org/route.html

  10. asp.net Npoi 使用

    HSSFWorkbook hssfworkbook = new HSSFWorkbook(); //增加 ExcelNPOI.SS.UserModel.ISheet SheetName = hssfw ...