我有以下代码读取维基百科转储文件(~50 GB)并根据请求提供页面:
defmodule Pages do def start_link(filename) do pid = spawn_link(__MODULE__,:loop,[filename]) Process.register(pid,:pages) pid end def next(xml_parser) do send(xml_parser,{:get_next,self()}) receive do {:next_page,page} -> page end end def loop(filename) do :xmerl_sax_parser.file(filename,event_fun: &event_fun/3,event_state: :top) loop_done end defp loop_done do receive do {:get_next,from} -> send(from,{:next_page,nil}) end loop_done end defp event_fun({:startElement,_,'page',_},:top) do :page end defp event_fun({:startElement,'text',:page) do :text end defp event_fun({:characters,chars},:text) do s = List.to_string(chars) receive do {:get_next,s}) end :text end defp event_fun({:endElement,:text) do :page end defp event_fun({:endElement,:page) do :top end defp event_fun({:endDocument},state) do receive do {:get_next,{:done}) end state end defp event_fun(_,state) do state end end
由于代码使用SAX解析器,我希望内存占用空间不变.当我尝试使用时首先阅读2000页
Enum.each(1..2000,fn(x) -> Pages.next(Process.whereis(:pages)); end)
:pages进程根据:observer.start()使用1,1 GB的内存.当我尝试读取10000页时,整个事情崩溃了:
Crash dump is being written to: erl_crash.dump...done eheap_alloc: Cannot allocate 5668310376 bytes of memory (of type "heap").
当我使用dump viewer打开erl_crash.dump时,我看到以下内容:
上面的代码有问题吗? GC不够快吗?虽然我可以看到每个进程的内存但它并没有告诉我很多.我怎么能看到这个记忆实际上去了哪里?
附:以下是今天崩溃转储的链接:https://ufile.io/becba.
原子数为14490,MsgQ为2表示:pages,0表示所有其他进程.
解决方法
默认的最大原子数略高于
1 million atoms.鉴于英文维基百科
has over 5 million articles和
xmerl seems to create an atom for each namespace URI,我认为它可能是罪魁祸首.
此外,尝试Elixir下面的代码失败只是一个“堆栈粉碎错误”.
Enum.each(1..2000000,fn (x) -> x |> Integer.to_string |> String.to_atom end)
但是如果我用环境变量ELIXIR_ERL_OPTIONS =“t 5000000”将原子限制提高到500万,那么问题就会消失.