0%

ErlangGC学习

概述

ErlangGC学习

ErlangGC学习

参考资料

erlang:process_info(Pid)的官方文档

Hamidreza Soleimani大佬的博客

Hamidreza Soleimani的GC博客

ErlangGC官方文档

琪哥ErlangGC

琪哥Erlang二进制GC

坚强大佬ErlangGC

代码如下

从趣学指南里面随便找了一个gen_server进程来模拟GC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
=======================kitty_gen_server.erl部分代码==========
make_tuple(Num) ->
gen_server:call(?SERVER, {?FUNCTION_NAME, Num}),
make_tuple_ok.

start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([]) ->
{ok, []}.

handle_call({make_tuple, Num}, _From, Cats) ->
{reply, [{X} || X <- lists:seq(1, Num)], Cats};


简单案例

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
1>erlang:process_info(Pid).

{total_heap_size,233},
{heap_size,233},
{stack_size,12},

{garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,size => 0}},
{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},

{minor_gcs,0} 小GC次数为0
{fullsweep_after,65535} 小GC触发65535后触发大GC

5> erlang:process_info(Pid, garbage_collection_info).

{garbage_collection_info,[{old_heap_block_size,0}, 老年堆最大值 0 表示还没初始化老年堆
{heap_block_size,233}, 青年堆最大值 等于上面的 heap_size
{mbuf_size,0},
{recent_size,0},
{stack_size,12},
{old_heap_size,0}, 老年堆当前值
{heap_size,46}, 青年堆当前值
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]}



6> kitty_gen_server:make_tuple(10). make 10个tuple

{total_heap_size,233},
{heap_size,233},
{stack_size,12},

{garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,size => 0}},
{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]}, 没有触发GC

{garbage_collection_info,[{old_heap_block_size,0},
{heap_block_size,233},
{mbuf_size,0},
{recent_size,0},
{stack_size,12},
{old_heap_size,0},
{heap_size,132}, heap_size 增加了 86
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]}

我们再make20个试试看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{total_heap_size,376},      
{heap_size,376},
{stack_size,12},

{garbage_collection_info,[{old_heap_block_size,0}, 触发大GC老年堆被干掉了
{heap_block_size,376}, 触发大GC了
{mbuf_size,0},
{recent_size,46},
{stack_size,12},
{old_heap_size,0}, 触发大GC老年堆被干掉了
{heap_size,136},
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]}



再make40个试试看

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
{total_heap_size,986},
{heap_size,376},
{stack_size,12},

{garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,size => 0}},
{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,1}]}, 触发了一次小GC


{garbage_collection_info,[{old_heap_block_size,610}, 初始化老年堆
{heap_block_size,376},
{mbuf_size,0},
{recent_size,40},
{stack_size,12},
这12word的数据是上次水位线下被RootSet直接或间接引用的数据没有被GC
它们从青年堆被放入老年堆
{old_heap_size,12},
{heap_size,130},
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]}


再make40个试试看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{total_heap_size,986},
{heap_size,376},
{stack_size,12},

{minor_gcs,2}]}, 又触发了一次小GC


{garbage_collection_info,[{old_heap_block_size,610},
{heap_block_size,376},
{mbuf_size,0},
{recent_size,96},
{stack_size,12},
这12word的数据是上次水位线下被RootSet直接或间接引用的数据没有被GC
它们从青年堆被放入老年堆 上面的12byte数据被GC掉了不然这里应该是24
{old_heap_size,12},
{heap_size,182},
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]}

再make 60个试试看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

{minor_gcs,3}]}, 第三次触发小GC

{garbage_collection_info,[{old_heap_block_size,610},
{heap_block_size,376},
{mbuf_size,0},
{recent_size,100},
{stack_size,12},
{old_heap_size,12},
{heap_size,350},
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]}

再make 60个试试看

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
{total_heap_size,1220},
{heap_size,610},
{stack_size,12},


{minor_gcs,4}]}, 第四次触发小GC


{garbage_collection_info,[{old_heap_block_size,610},


没有大GC 但是 MAXHEAP增加了
因为把水位线下的数据移入老年堆以后剩下的
青年数据382大于前面的376 执行 MAXHEAP 增长函数
代码里面其实如果这里不是382 就算是370也会触发 MAXHEAP 增长函数(剩余空间小于25%)

{heap_block_size,610},
{mbuf_size,0},
{recent_size,12},
{stack_size,12},
{old_heap_size,12},
{heap_size,382},
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]}

再make 60个试试看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{total_heap_size,1220},
{heap_size,610},
{stack_size,12},
{minor_gcs,5} 第五次触发小GC

{garbage_collection_info,[{old_heap_block_size,610},
{heap_block_size,610},
{mbuf_size,0},
{recent_size,66},
{stack_size,12},
{old_heap_size,12},
{heap_size,316},
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]}

模拟实际案例

上面只是ErlangGC一些很简单的基础知识
现在来模拟一下在业务实践中两个进程之前互相收发大消息

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
self()的数据  模拟玩家进程
1> erlang:process_info(self(), [garbage_collection_info,garbage_collection,memory])
[{garbage_collection_info,[{old_heap_block_size,0},
{heap_block_size,987},
{mbuf_size,90},
{recent_size,460},
{stack_size,26},
{old_heap_size,0},
{heap_size,935},
{bin_vheap_size,4},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]},
{garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,size => 0}},
{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{memory,8720}]

Pid 模拟大富翁进程
erlang:process_info(Pid, [garbage_collection_info,garbage_collection,memory]).

[{garbage_collection_info,[{old_heap_block_size,0},
{heap_block_size,233},
{mbuf_size,0},
{recent_size,12},
{stack_size,12},
{old_heap_size,0},
{heap_size,12},
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]},
{garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,size => 0}},
{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{memory,2736}]

%% 玩家进程从大富翁进程的ETS里面取出一个特别大的数据
kitty_gen_server:make_tuple(100000).


erlang:process_info(self(), [garbage_collection_info,garbage_collection,memory]).
[{garbage_collection_info,[{old_heap_block_size,2586},
{heap_block_size,514838},
{mbuf_size,0},
{recent_size,400006},
{stack_size,26},
{old_heap_size,179},
{heap_size,514809},
{bin_vheap_size,1},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,4},
{bin_old_vheap_block_size,46422}]},
{garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,size => 0}},
{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,4}]},
{memory,4140216}]


erlang:process_info(Pid, [garbage_collection_info,garbage_collection,memory]).
[{garbage_collection_info,[{old_heap_block_size,0},
{heap_block_size,514838},
{mbuf_size,0},
{recent_size,236348},
{stack_size,12},
{old_heap_size,0},
{heap_size,400034},
{bin_vheap_size,0},
{bin_vheap_block_size,46422},
{bin_old_vheap_size,0},
{bin_old_vheap_block_size,46422}]},
{garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,size => 0}},
{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{memory,4119576}]

Erlang进程间传递消息是采用复制的方式,可以看到上面玩家进程和大富翁进程内存都增加了
这时调用一次大GC是比较好的做法
erlang:garbage_collect(). 默认是同步大GC
erlang:garbage_collect(Pid).