我有以下代码读取维基百科转储文件(~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
Run Code Online (Sandbox Code Playgroud)
由于代码使用SAX解析器,我期望内存占用量不变.当我尝试使用时首先阅读2000页
Enum.each(1..2000, fn(x) -> Pages.next(Process.whereis(:pages)); end)
Run Code Online (Sandbox Code Playgroud)
根据内存的:pages过程使用.当我尝试读取10000页时,整个事情崩溃了:1,1 GB:observer.start()
Crash dump is being written to: erl_crash.dump...done
eheap_alloc: Cannot allocate 5668310376 bytes of memory (of type "heap").
Run Code Online (Sandbox Code Playgroud)
当我erl_crash.dump使用dump viewer 打开时,我看到以下内容:

上面的代码有问题吗?GC不够快吗?虽然我可以看到每个进程的内存但它并没有告诉我很多.我怎么能看到这个记忆实际上去了哪里?
PS这是今天崩溃转储的链接:https://ufile.io/becba.原子数为14490,其中所有其他过程MsgQ为2 :pages和0.
小智 2
默认的最大原子数略高于100 万个原子。鉴于英语维基百科有超过 500 万篇文章,并且 xmerl 似乎为每个名称空间 URI 创建一个原子,我认为它可能是罪魁祸首。
另外,在 Elixir 上尝试下面的代码失败,只是出现“堆栈崩溃错误”。
Enum.each(1..2000000, fn (x) ->
x
|> Integer.to_string
|> String.to_atom
end)
Run Code Online (Sandbox Code Playgroud)
但是,如果我使用环境变量将原子限制提高到 500 万ELIXIR_ERL_OPTIONS="+t 5000000",问题就消失了。