Утечки памяти

321 views
Skip to first unread message

Денис Парыгин

unread,
Jul 30, 2014, 12:43:24 AM7/30/14
to erlang-...@googlegroups.com
Всем привет!

Erlang не отдает память системе.
Erlang 17.1 и 16B3 rev.2 ведут себя одинаково.
При порождении процессов использую

Options = [{spawn_opt, [{fullsweep_after, 0}]}],
gen_server
:start_link({local, ?SERVER}, ?MODULE, [], Options).

В процессах память не утекает (судя по observer), но накапливается Binary и
erlang:garbage_collect()
не помогет.

Max Lapshin сообщал о том что опция +MBacul 0 улучшает ситуацию (если я правильно понял), да улучшает но не много.

За 16 часов работы erlang сожрал более 500 мегабайт памяти.

Может кто знает куда копать?

Sergey Abramyan

unread,
Jul 30, 2014, 12:48:50 AM7/30/14
to erlang-...@googlegroups.com, Денис Парыгин
Как работаете с бинарями? Бинари какого размера?
-- 
Sergey Abramyan
--
Вы получили это сообщение, поскольку подписаны на группу "Erlang по-русски".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес erlang-russia...@googlegroups.com.
Чтобы отправлять сообщения в эту группу, отправьте письмо на электронный адрес erlang-...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.

Денис Парыгин

unread,
Jul 30, 2014, 12:57:31 AM7/30/14
to
Получаю пакеты от клиента используя ranch.
Пакеты до 250 байт, но много.

Работаю так:

handle_info({tcp, Socket, Data}, State = #connect_state{socket = Socket, transport = _Transport}) ->
 
% lager:warning("[proto] raw data: ~ts", [Data]),
 
{TailData, NormalState} = case State#connect_state.tail of
        
<<>> -> {Data, State};
        _
->
            
MergeData = iolist_to_binary([State#connect_state.tail, Data]),
            
MergeState = State#connect_state{tail = <<>>},
            
{MergeData, MergeState}
 
end,
 
case binary:last(TailData) of
   
0 ->
      
BinPackets = binary:split(TailData, <<0>>, [global, trim]),
      
NewState = stream_processing(BinPackets, NormalState);
   _
->
   
Buffer = binary:split(TailData, <<0>>, [global, trim]),
   
Tail = lists:last(Buffer),
   
BinPackets = lists:delete(Tail, Buffer),
   
TailState = State#connect_state{tail = Tail},
   
NewState = stream_processing(BinPackets, TailState)
 
end,
 
{noreply, NewState, ?TIMEOUT};

stream_processing([], State) ->
State;
stream_processing([Pkt | Tail], State) ->
case jsonx:decode(Pkt, [{format, proplist}]) of
{error, X, Y} ->
lager:error("Error JSON decode [~p, ~p]. JSON: ~ts", [X, Y, Pkt]),
State;
Packet -> 
NewState = case proplists:get_value(?__TYPE, Packet) of
?__LOGIN ->
login(Packet, State);
PktType when State#connect_state.authorized ->
Cmd = proplists:get_value(?__COMMAND, Packet),
packet_processing(PktType, Cmd, Packet, State);
_UnauthorizedPkt ->
lager:warning("Unauthorized packet type ~p. Cmd: ~p. Dump: ~p", [_UnauthorizedPkt, proplists:get_value(?__COMMAND, Packet), Pkt]),
State
end,
stream_processing(Tail, NewState)
end.

Есть еще куски разные.
Есть sqlite3 driver

Спросите конкретнее. Все расскажу:)

среда, 30 июля 2014 г., 10:48:50 UTC+6 пользователь Sergey Abramyan написал:

Денис Фахртдинов

unread,
Jul 30, 2014, 1:56:18 AM7/30/14
to erlang-...@googlegroups.com
Я бы проверил два варианта утечки памяти в первую очередь.

Первый - утечка бинарей. Вы используете binary:split, который всегда возвращает список ссылочных бинарей. Если такая ссылка пересылается в другой процесс, то GC не соберет исходный бинарь пока не будут "отGCшены" все процессы, которые этот бинарь каким-либо образом трогали. Чтобы проверить этот вариант на валидность, попробуйте этот код: https://github.com/shizzard/dotfiles/blob/master/user_default.erl#L175. Вызов hibin(512) вернет список пидов процессов, которые держат больше 512 байт бинарей.

Второй - простое распухание куч процессов. Если ваши бинари конвертятся в другие типы данных, то под эти данные процессы получают кучу большего размера. Heap процессов не умеет уменьшаться в общем случае, только в случае hibernate процесса. Насколько я знаю, hibernate - достаточно дорогое удовольствие, поэтому можно получение и обработку пакета отдавать короткоживущему процессу, который умрет сразу же после обработки. Конечно, это имеет смысл только в случае, если ваши коннекты живут долго и таких процессов много. Проверить можно тем же кодом, ВМ умеет отдавать размеры куч процессов.

В код не вникал особо, простите.


Есть sqlite2 driver

Спросите конкретнее. Все расскажу:)

среда, 30 июля 2014 г., 10:48:50 UTC+6 пользователь Sergey Abramyan написал:
Как работаете с бинарями? Бинари какого размера?
-- 
Sergey Abramyan

vvv

unread,
Jul 30, 2014, 2:02:24 AM7/30/14
to erlang-...@googlegroups.com
Память утекает или доходит до фиксированного размера, а потом не отдаётся?

Если первое, то нужно внимательно следить за данными, к которым применяется binary:split или сопоставление, gc не будет удалять их, если есть ссылки на данные.

Если второе и erlang:garbage_collect() не помогает, то эрланг зарезервировал эту память под будущие данные. GC не всегда запускается, если лишняя память есть.
Попробуйте замеры сделать с помощью erlang:memory(), если binary не сильно скачет, то это нормально.

У меня похожая ситуация, только данные 25-250кб, binary в пределах 1.5 - 2гб

среда, 30 июля 2014 г., 8:43:24 UTC+4 пользователь Денис Парыгин написал:

fred partanskii

unread,
Jul 30, 2014, 2:25:41 AM7/30/14
to erlang-...@googlegroups.com

Используете ли вы gen_server?

30 июля 2014 г. 8:57 пользователь "Денис Парыгин" <denn...@gmail.com> написал:
Как работаете с бинарями? Бинари какого размера?

Денис Парыгин

unread,
Jul 30, 2014, 3:04:10 AM7/30/14
to
Да, использую gen_server

pfi79

unread,
Jul 30, 2014, 3:38:58 AM7/30/14
to erlang-...@googlegroups.com
Если память копится в gen_server, можно попробовать вызвать сборку мусора из другого процесса по pid gen_server

среда, 30 июля 2014 г., 11:04:10 UTC+4 пользователь Денис Парыгин написал:
Да, использую gen_server

Kirill Zaborsky

unread,
Jul 30, 2014, 3:43:20 AM7/30/14
to Erlang в России
Довольно странный совет, исходя из исходной предпосылки "erlang:garbage_collect() не помогет"

С уважением,
Кирилл Заборский.


30 июля 2014 г., 11:38 пользователь pfi79 <fredp...@gmail.com> написал:
Если память копится в gen_server, можно попробовать вызвать сборку мусора из другого процесса по pid gen_server

среда, 30 июля 2014 г., 11:04:10 UTC+4 пользователь Денис Парыгин написал:
Да, использую gen_server

--

Max Lapshin

unread,
Jul 30, 2014, 4:19:08 AM7/30/14
to erlang-...@googlegroups.com
Поставьте recon и проверьте: recon_alloc:usage

Вернется КПД аллокатора.

mprize

unread,
Jul 30, 2014, 4:38:03 AM7/30/14
to erlang-...@googlegroups.com
а binary:copy не поможет?

-- 
BR

Денис Парыгин

unread,
Jul 30, 2014, 5:09:30 AM7/30/14
to erlang-...@googlegroups.com


среда, 30 июля 2014 г., 14:19:08 UTC+6 пользователь Max Lapshin написал:
Поставьте recon и проверьте: recon_alloc:usage

Вернется КПД аллокатора.

вот:
2> recon_alloc:memory(used).
22168296
3> recon_alloc:memory(usage).
0.5682786907905447
4> tmsUtils:uptime().
"0 days, 0 hours, 3 minutes and 58 seconds"
5>

О чем это может сказать? 

Денис Парыгин

unread,
Jul 30, 2014, 5:14:26 AM7/30/14
to erlang-...@googlegroups.com
Попробую.

среда, 30 июля 2014 г., 14:38:03 UTC+6 пользователь mprize написал:

pfi79

unread,
Jul 30, 2014, 5:50:37 AM7/30/14
to erlang-...@googlegroups.com
erlang:garbage_collect(), не помогает - это нормально
А вот надо попробовать: полчить все pid'ы и map'ом вызвать erlang:garbage_collect(pid).


среда, 30 июля 2014 г., 11:43:20 UTC+4 пользователь Kirill Zaborsky написал:

Kirill Zaborsky

unread,
Jul 30, 2014, 5:55:17 AM7/30/14
to Erlang в России
Если recon уже был установлен, то логично предположить, что recon:bin_leak/1 был испробован, но можно, конечно и руками всё сделать.

С уважением,
Кирилл Заборский.


30 июля 2014 г., 13:50 пользователь pfi79 <fredp...@gmail.com> написал:

Danil A. Zagoskin

unread,
Jul 30, 2014, 5:56:22 AM7/30/14
to erlang-...@googlegroups.com
То, что usage 57% при 22 МБ ценных данных — норма. Хотя о какой утечке может быть речь, если перерасход всего 20 МБ?

У меня еще есть нерешенная проблема с утечкой системной памяти. Замер можно сделать так:
M = erlang:memory(), V=fun(F) -> proplists:get_value(F, M) end, V(system) - V(atom) - V(binary) - V(code) - V(ets).

Еще покажите, какие у вас драйверы работают: erl_ddll:info().


--

Денис Парыгин

unread,
Jul 30, 2014, 6:39:24 AM7/30/14
to
4> erl_ddll:info().
[{"sqlite3_drv",
  [{processes,[{<0.98.0>,1},{<0.101.0>,1},{<0.102.0>,1}]},
   {driver_options,[]},
   {port_count,3},
   {linked_in_driver,false},
   {permanent,false},
   {awaiting_load,[]},
   {awaiting_unload,[]}]}]

5> M = erlang:memory(), V=fun(F) -> proplists:get_value(F, M) end, V(system) - V(atom) - V(binary) - V(code) - V(ets).
26517716

При этом erl в системе жрет уже 69 Mb

Денис Парыгин

unread,
Aug 1, 2014, 1:19:50 AM8/1/14
to erlang-...@googlegroups.com
Все спасибо за советы.
Разбирательство в процессе....
Как разберусь сообщу о найденой проблеме и ее решении

среда, 30 июля 2014 г., 10:43:24 UTC+6 пользователь Денис Парыгин написал:

Денис Парыгин

unread,
Aug 25, 2014, 11:02:05 PM8/25/14
to erlang-...@googlegroups.com
Во всем виновата библа erlang_sqlite3 именно в ней текут бинари.


среда, 30 июля 2014 г., 10:43:24 UTC+6 пользователь Денис Парыгин написал:
Всем привет!

fred partanskii

unread,
Aug 26, 2014, 12:21:09 PM8/26/14
to erlang-...@googlegroups.com

А где там конкретно? Не капнул?
26 авг. 2014 г. 7:02 пользователь "Денис Парыгин" <denn...@gmail.com> написал:

Денис Парыгин

unread,
Aug 26, 2014, 12:43:00 PM8/26/14
to
Не копал. Наткнулся в сети, что там есть проблемы в си-коде вроде. Туда не полез.
В итоге избавился от библы в пользу emysql - шикарная библа оказалась, хоть они себя и ругают за неясный апи и прочие мелочи.
Меня устроило все. И самое главное там такое все параллельное :)

вторник, 26 августа 2014 г., 22:21:09 UTC+6 пользователь pfi79 написал:
Reply all
Reply to author
Forward
0 new messages