我有以下代码读取维基百科转储文件(~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, from} -> send(from, {:next_page, s})
end
:text
end
defp event_fun({:endElement, _, 'text', _}, _, :text) do
:page
end
defp event_fun({:endElement, _, 'page', _}, _, :page) do
:top
end
defp event_fun({:endDocument}, _, state) do
receive do
{:get_next, from} -> send(from, {:done})
end
state
end
defp event_fun(_, _, state) do
state
end
end
由于代码使用SAX
解析器,我希望内存占用空间不变。当我尝试使用
Enum.each(1..2000, fn(x) -> Pages.next(Process.whereis(:pages)); end)
:pages
进程根据1,1 GB
使用:observer.start()
内存。当我尝试阅读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不够快吗?虽然我可以看到每个进程的内存,但它并没有告诉我很多。我怎么能看到这个记忆的实际位置呢?
P.S。以下是今天崩溃转储的链接:https://ufile.io/becba。
原子数为14490,MsgQ
为:pages
为2,所有其他进程为0。
答案 0 :(得分:2)
默认的最大原子数略高于1 million atoms。鉴于英语维基百科has over 5 million articles和xmerl seems to create an atom for each namespace URI,我认为这可能是罪魁祸首。
此外,在Elixir上尝试下面的代码失败,只有#34;堆栈粉碎错误"。
Enum.each(1..2000000, fn (x) ->
x
|> Integer.to_string
|> String.to_atom
end)
但是如果我使用环境变量ELIXIR_ERL_OPTIONS="+t 5000000"
将原子限制提高到500万,那么问题就会消失。