2016-08-18 44 views
6

我有下面的代码读取维基百科转储文件(〜50 GB),并根据要求提供了网页:使用xmerl读取大型XML文件崩溃节点

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过程使用根据:observer.start()的内存1,1 GB阅读第2000页。当我尝试阅读10000页,整个事情崩溃:

Crash dump is being written to: erl_crash.dump...done 
eheap_alloc: Cannot allocate 5668310376 bytes of memory (of type "heap"). 

当我使用dump观众我看到下面打开erl_crash.dumpenter image description here

出毛病了上面的代码? GC不够快吗?虽然我可以看到每个进程的内存,但它并没有告诉我很多。我怎样才能看到这个记忆究竟在哪里?

P.S.这里是从今天起的一个崩溃转储的链接:https://ufile.io/becba。 原子的数量是14490,MsgQ对于:pages是2,对于所有其他过程是0。

+0

当它崩溃时会得到什么错误信息? – legoscia

+1

我添加了错误信息 – damluar

+1

也许它正在填充原子表?请参阅xmerl上的[this thread](http://erlang.org/pipermail/erlang-questions/2007-March/025592.html)。 “我发现的一个问题是,xmerl **会为每个元素名称或**名称空间URI **产生新的原子**,它将从输入中解析出来。” 那里的答案建议使用erlsom sax解析器而不是xmerl。 –

回答

1

原子的默认最大数量略高于1 million atoms。鉴于英文维基百科has over 5 million articlesxmerl seems to create an atom for each namespace URI,我认为它可能是罪魁祸首。

另外,在Elixir上尝试下面的代码会失败,只会出现“堆栈粉碎错误”。

Enum.each(1..2000000, fn (x) -> 
    x 
    |> Integer.to_string 
    |> String.to_atom 
end) 

但如果我提出了原子的限制像500万与环境变量ELIXIR_ERL_OPTIONS="+t 5000000",问题消失。

+1

这看起来像是在xmerl中的一个大规模的监督。考虑到它像DOS最简单的方式来执行erlang服务,你会认为它们使用了二进制文件或字符串。 – rozap