简单的理解:
- hibernate 可以令处于 idle 状态的 erlang 进程马上进行 gc,因为进程处于 receive 状态下是不会 gc 的。
- 在性能调优时发现,gc 对于消息发送速度的影响还是非常大的。
关于 erlang gc 问题也可以参看这个
Recently, as part of RabbitMQ server development, we ran into an interesting issue regarding Erlang’s per-process garbage collection. If a process is idle — not doing any work at all, simply waiting for an external event — then its garbage-collector will not run until it starts working again. The solution is to hibernate idle processes, which causes a very aggressive garbage-collection run and puts the process into a suspended state, from which it will wake when a message next arrives.
所以查看 rabbitMQ 源代码可以看到里面的 gen_server/fsm 等进程统统都会使用 hibernate 。
下面解释下如何让进程 hibernate ,这里需要注意的是一个进程 hiberate 后,会清空 stack 栈,也就是说之前的调用关系全部没有了,也就是说hibernate 所在的函数永远不会返回,待有新消息时,进程从 hibernate(M,F,A) 里指定的 M:F 处开始运行。
gen_server 和 gen_fsm 都提供了 hibernate 的方式:
第一种: 在 gen_server 或 gen_fsm 回调接口返回时,指定 hibernate 。
1 |
|
实际这个 Time1 可以指定为 hibernate,则重新回到 main loop 时会直接 hibernate 。
这种方法可能使你的进程频繁 hibernate 又被唤醒,效率不是很好。
正确的方式应该是,当你预期你的进程一段时间内不会收到消息才 hibernate 。
所以推荐下面的方法,直接看下示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
总体上,是让进程在 main_loop 里先 timeout ,也就是证明一段时间内都没有消息到达,然后 gen_fsm 进程是发送 {'gen_event',timeout} 消息处理 timeout 事件的,所以要在每个 Mod:StateName 处都加个处理 timeout 的分支,来进行 timeout 处理,也就是 hibernate 掉自己,重新进入的是 enter_loop 函数,这个东西很有用呐,大家可以查看源码,我就不多说了。
另外,示例中可以看到,hibernate 会清空 stack,但是不会影响你的进程字典和 ets 等其它东西,可以放心用了。