2016-08-18 2 views
6

Ich habe den folgenden Code, der eine wikipedia-Dump-Datei (~ 50 GB) und liefert Seiten auf Anfrage lautet:große XML-Datei mit xmerl Lesen stürzt der Knoten

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 

Da der Code verwendet SAX Parser würde ich erwarten, konstanter Speicherbedarf mit

Enum.each(1..2000, fn(x) -> Pages.next(Process.whereis(:pages)); end) 

der :pages Prozess verwendet 1,1 GB Speicher nach :observer.start() Wenn ich versuche ersten 2000 Seiten zu lesen. Wenn ich versuche, 10000 Seiten zu lesen, die ganze Sache Abstürze:

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

Wenn ich erl_crash.dump mit Dump-Viewer zu öffnen Ich sehe folgendes: enter image description here

Stimmt etwas nicht mit dem Code oben? Ist GC nicht schnell genug? Obwohl ich die Erinnerung pro Prozess sehen kann, sagt sie mir nicht viel. Wie kann ich sehen, wohin diese Erinnerung tatsächlich geht?

P.S. Hier ist ein Link zu einem Absturzspeicher von heute: https://ufile.io/becba. Die Anzahl der Atome ist 14490, die MsgQ ist 2 für :pages und 0 für alle anderen Prozesse.

+0

Welche Fehlermeldung erhalten Sie, wenn es abstürzt? – legoscia

+1

Ich habe die Fehlermeldung hinzugefügt – damluar

+1

Vielleicht füllt es die Atom-Tabelle? Siehe [this thread] (http://erlang.org/pipermail/erlang-questions/2007-March/025592.html) auf xmerl. "Was ich finde, ist ein Problem, dass xmerl ** neue Atome ** für jeden Elementnamen oder ** Namespace-URI ** erzeugt, die es von der Eingabe analysiert." Die Antwort dort schlägt vor, den Erlsom Sax Parser anstelle von Xmerl zu verwenden. –

Antwort

1

Die voreingestellte maximale Anzahl der Atome liegt etwas über 1 million atoms. Angesichts der englischen Wikipedia has over 5 million articles und xmerl seems to create an atom for each namespace URI, denke ich, dass es der Schuldige sein kann.

Auch, den folgenden Code auf Elixir zu versuchen, scheitert mit nur einem "Stack-Smashing-Fehler".

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

Aber wenn ich das Atom Grenze zu so etwas wie 5 Millionen mit der Umgebungsvariablen ELIXIR_ERL_OPTIONS="+t 5000000" erhöhen, verschwindet das Problem.

+1

Dies scheint eine massive Aufsicht in Xmerl. Wenn man bedenkt, dass es der einfachste Weg ist, einen Erlang-Dienst zu starten, würde man meinen, sie hätten Binärdateien oder Strings verwendet. – rozap