当Erlang全扫描时,内存会立即下降,但在很短的时间内它会上升到原来的峰值,然后下降。
当我在将gc转换为TestPid 3分钟或更长时间后停止循环以创建内存时。内存会上升到最初的峰值,然后在我开始循环后下降。
它是如何工作的?
这是我的简单测试代码。
ts.erl
-module(ts).
-behaviour(gen_server).
%% API
-export([
start/0,
stop/0,
gc/0, loop_cnt/1
]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(BASE_CNT, 10000).
-define(ONE_LOOP_CNT, 200).
-record(state, {loop_cnt = 0, one_loop_cnt = 0}).
start() ->
gen_server:start({local, ?SERVER}, ?MODULE, [], []).
stop() ->
gen_server:cast(?SERVER, stop).
gc() ->
gen_server:cast(?SERVER, gc).
loop_cnt(Cnt) when is_number(Cnt) ->
gen_server:cast(?SERVER, {loop_cnt, Cnt}).
init([]) ->
io:format("start mem ~n"),
erlang:send_after(1, self(), start_add_mem),
{ok, #state{one_loop_cnt = ?ONE_LOOP_CNT}, 0}.
handle_call(_Req, _From, State) ->
{noreply, State}.
handle_cast(start_add_mem, State) ->
io:format("cast start_add_mem~n"),
{noreply, State};
handle_cast(gc, State) ->
io:format("garbage_info beforegc ~w ~n", [erlang:process_info(self(), garbage_collection)]),
{Us, _} = timer:tc(erlang, garbage_collect, [self()]),
io:format("do_gc cost:~wms ~n", [Us / 1000]),
{noreply, State};
handle_cast({loop_cnt, Cnt}, State) ->
io:format("set loop_cnt ~w ~n", [Cnt]),
{noreply, State#state{one_loop_cnt = Cnt}};
handle_cast(stop, State) ->
{stop, normal, State}.
handle_info(start_add_mem, #state{loop_cnt = Cnt, one_loop_cnt = OneLoopCnt} = State) ->
erlang:send_after(1000, self(), start_add_mem),
StartCnt = Cnt rem 100,
case Cnt rem 30 =:= 0 of
true ->
io:format("garbage_info ~w ~n", [erlang:process_info(self(), garbage_collection)]);
false ->
ok
end,
do_add_mem(StartCnt, OneLoopCnt),
try
{_, L} = erlang:process_info(self(), garbage_collection),
case lists:keyfind(minor_gcs, 1, L) of
false ->
io:format("gc find mingcs error~w ~n", [erlang:process_info(self(), garbage_collection)]);
{_, GcCount} ->
case GcCount =:= 0 of
true ->
io:format("mabay_trigger gc ~w ~n", [erlang:process_info(self(), garbage_collection)]);
_ ->
ok
end
end
catch _A:_B ->
io:format("gc print error ~w ~w ~n", [erlang:process_info(self(), garbage_collection), {_A, _B}]),
ok
end,
{noreply, State#state{loop_cnt = Cnt + 1}};
handle_info(_Req, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
do_add_mem(StartCnt, OneLoopCnt) ->
lists:foreach(
fun(I) ->
Dict = dict:new(),
NewDict = lists:foldl(
fun(J, AccDict) ->
dict:store(J, I, AccDict)
end, Dict, lists:seq(1, OneLoopCnt)),
L = lists:seq(1, OneLoopCnt),
case random:uniform() > 0.5 of
true ->
erlang:put({tm, I}, {NewDict, L});
false ->
erlang:put({tm, I}, undefined)
end
end, lists:seq(StartCnt * ?BASE_CNT, StartCnt * ?BASE_CNT + ?BASE_CNT)),
ok.
1条答案
按热度按时间qni6mghb1#
我认为峰值是GC在运行主要GC时创建新堆,第二个谷值是释放前一个堆的时候。
考虑到每个进程在不同的时间执行其GC,并且您通常没有具有5GB堆的进程,您不会(或者不应该)在生产系统中注意到它。
关于Erlang的世代GC的文档非常详细,我认为它值得仔细阅读。
此外,您还可以使用
trace
在GC开始或结束时获取消息:或者使用process_info
的total_heap_size
和heap_size
。