Clojure Leining REPL OutOfMemoryError Пространство кучи Java
Я пытаюсь проанализировать довольно маленький (< 100 МБ) XML-файл с помощью:
(require '[clojure.data.xml :as xml]
'[clojure.java.io :as io])
(xml/parse (io/reader "data/small-sample.xml"))
и я получаю ошибку:
OutOfMemoryError Java heap space
clojure.lang.Numbers.byte_array (Numbers.java:1216)
clojure.tools.nrepl.bencode/read-bytes (bencode.clj:101)
clojure.tools.nrepl.bencode/read-netstring* (bencode.clj:153)
clojure.tools.nrepl.bencode/read-token (bencode.clj:244)
clojure.tools.nrepl.bencode/read-bencode (bencode.clj:254)
clojure.tools.nrepl.bencode/token-seq/fn--3178 (bencode.clj:295)
clojure.core/repeatedly/fn--4705 (core.clj:4642)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:60)
clojure.lang.RT.seq (RT.java:484)
clojure.core/seq (core.clj:133)
clojure.core/take-while/fn--4236 (core.clj:2564)
Вот мой проект. Clj:
(defproject dats "0.1.0-SNAPSHOT"
...
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/data.xml "0.0.7"]
[criterium "0.4.1"]]
:jvm-opts ["-Xmx1g"])
Я попытался установить LEIN_JVM_OPTS и JVM_OPTS в моем.bash_profile, но безуспешно.
Когда я попробовал следующий project.clj:
(defproject barber "0.1.0-SNAPSHOT"
...
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/data.xml "0.0.7"]
[criterium "0.4.1"]]
:jvm-opts ["-Xms128m"])
Я получаю следующую ошибку:
Error occurred during initialization of VM
Incompatible minimum and maximum heap sizes specified
Exception in thread "Thread-5" clojure.lang.ExceptionInfo: Subprocess failed {:exit-code 1}
Любая идея, как я мог бы увеличить размер кучи для моей реплики leiningen?
Благодарю.
2 ответа
Любая форма, оцениваемая на верхнем уровне реплики, реализуется полностью в результате шага печати цикла Read-Eval-Print-Loop. Он также хранится в куче, так что вы можете получить к нему доступ через *1.
если вы храните возвращаемое значение следующим образом:
(def parsed (xml/parse (io/reader "data/small-sample.xml")))
это немедленно возвращается, даже для файла размером в сотни мегабайт (я проверил это локально). Затем вы можете выполнить итерацию по результату, который реализуется полностью, когда он анализируется из входного потока, путем итерации по дереву clojure.data.xml.Element, которое возвращается.
Если вы не держитесь за элементы (связывая их так, чтобы они все еще были доступны), вы можете выполнять итерацию по всей структуре, не используя больше оперативной памяти, чем требуется для хранения одного узла дерева xml.
user> (time (def n (xml/parse (clojure.java.io/reader "/home/justin/clojure/ok/data.xml"))))
"Elapsed time: 0.739795 msecs"
#'user/n
user> (time (keys n))
"Elapsed time: 0.025683 msecs"
(:tag :attrs :content)
user> (time (-> n :tag))
"Elapsed time: 0.031224 msecs"
:catalog
user> (time (-> n :attrs))
"Elapsed time: 0.136522 msecs"
{}
user> (time (-> n :content first))
"Elapsed time: 0.095145 msecs"
#clojure.data.xml.Element{:tag :book, :attrs {:id "bk101"}, :content (#clojure.data.xml.Element{:tag :author, :attrs {}, :content ("Gambardella, Matthew")} #clojure.data.xml.Element{:tag :title, :attrs {}, :content ("XML Developer's Guide")} #clojure.data.xml.Element{:tag :genre, :attrs {}, :content ("Computer")} #clojure.data.xml.Element{:tag :price, :attrs {}, :content ("44.95")} #clojure.data.xml.Element{:tag :publish_date, :attrs {}, :content ("2000-10-01")} #clojure.data.xml.Element{:tag :description, :attrs {}, :content ("An in-depth look at creating applications \n with XML.")})}
user> (time (-> n :content count))
"Elapsed time: 48178.512106 msecs"
459000
user> (time (-> n :content count))
"Elapsed time: 86.931114 msecs"
459000
;; redefining n so that we can test the performance without the pre-parsing done when we counted
user> (time (def n (xml/parse (clojure.java.io/reader "/home/justin/clojure/ok/data.xml"))))
"Elapsed time: 0.702885 msecs"
#'user/n
user> (time (doseq [el (take 100 (drop 100 (-> n :content)))] (println (:tag el))))
:book
:book
.... ;; output truncated
"Elapsed time: 26.019374 msecs"
nil
user>
Обратите внимание, что только когда я впервые запрашиваю подсчет содержимого n (что приводит к анализу всего файла), возникает огромная задержка. Если я дозу через подразделы структуры, это происходит очень быстро.
Я не так много знаю о lein, но в mvn вы можете сделать следующее:
mvn -Dclojure.vmargs="-d64 -Xmx2G" clojure:nrepl
(Я не думаю, что это имеет значение, но я всегда видел это с капиталом G, чувствителен ли он к регистру?)
Загрузка 100 МБ данных в память не должна быть проблемой. Я регулярно маршрутизирую данные в ГБ через свои проекты.
Я всегда использую сервер 64-битной версии для больших куч, и это, кажется, то, что они делают здесь:
Варианты JVM с использованием Leiningen
Я думаю, что большая проблема заключается в том, что, поскольку вы написали это, это может быть оценено во время компиляции. Вам нужно обернуть этот вызов в функцию и отложить его выполнение. Я думаю, что компилятор пытается прочитать этот файл, и это, вероятно, не то, что вы хотите. Я знаю, что с mvn вы получаете разные настройки памяти для компиляции и запуска, и вы можете получить это тоже.