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. MyCat入门指南

    入门篇 1.       安装 1.1从https://github.com/MyCATApache/Mycat-download下载压缩包 1.2解压缩后复制到相应目录下面,比如/usr/local ...

  2. 由浅入深了解EventBus:(三)

    原理 EventBus的核心工作机制如下图 在EventBus3.0架构图: EventBus类 在EventBus3.0框架的内部,核心类就是EventBus,订阅者的注册/订阅,解除注册,以及事件 ...

  3. poj1273&&hdu1532

    题解: 双倍经验 dinic模板题 然后ff过不了... 代码: #include<cstdio> #include<cstring> #include<algorith ...

  4. MySQL Block Nested-Loop Join(BNL)

    5.5 版本之前,MySQL本身只支持一种表间关联方式,就是嵌套循环(Nested Loop).如果关联表的数据量很大,则join关联的执行时间会非常长.在5.5以后的版本中,MySQL通过引入BNL ...

  5. The disadvantage for manager has a part-time job as a trainer

    At present, most companies with in-house trainers take the development model of "full-time trai ...

  6. HAWQ + MADlib 玩转数据挖掘之(五)——奇异值分解实现推荐算法

    一.奇异值分解简介 奇异值分解简称SVD(singular value decomposition),可以理解为:将一个比较复杂的矩阵用更小更简单的三个子矩阵的相乘来表示,这三个小矩阵描述了大矩阵重要 ...

  7. 如何预编译ASP.Net程序

    打开Developer Command Prompt,执行命令 aspnet_compiler -v \Target  -p 源文件夹地址 -f 目标文件夹地址

  8. Failed to resolve: com.android.support:appcompat-v7:27.+ 报错解决方法

    最近在学习Android方面的编程,这个过程中出现了许多的错误,其中最多的错误是出现在构建工具进行编译的时候.这里分析一个出现的错误,Failed to resolve: com.android.su ...

  9. Loj 2534 异或序列

    Loj 2534 异或序列 考虑莫队离线处理.每加一个数,直接询问 \(a[x]\oplus k\) 的前/后缀数目即可,减同理. 利用异或的优秀性质,可以维护异或前缀和,容易做到每次 \(O(1)\ ...

  10. BZOJ2753 SCOI2012 滑雪与时间胶囊 【最小生成树】*

    BZOJ2753 SCOI2012 滑雪与时间胶囊 Description a180285非常喜欢滑雪.他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同时也是景点),而且每个景点都有 ...