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.

Когда я попробовал следующий проект.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 repl?

Спасибо.


person Nicolas M.    schedule 07.08.2013    source источник
comment
Сохраняются ли некоторые данные (результат синтаксического анализа XML) в массиве? Если да, то насколько он велик?   -  person Chiron    schedule 07.08.2013
comment
Вы вызываете вторую строку из REPL?   -  person Leon Grapenthin    schedule 07.08.2013
comment
Chiron: Пока не храню XML в какой-либо структуре данных. Просто вызовите метод разбора, как в моем посте. Играпентин: Да, я вызываю строку разбора из REPL. Файл весит 50мб, не разархивирован.   -  person Nicolas M.    schedule 07.08.2013
comment
Как я заметил в своем ответе, все вещи, возвращаемые на верхнем уровне repl, сохраняются (и полностью оцениваются, даже если в противном случае они были бы ленивыми), сначала сохраняются как * 1, затем как * 2 и т. д.   -  person noisesmith    schedule 08.08.2013


Ответы (2)


Любая форма, оцениваемая на верхнем уровне repl, реализуется полностью в результате шага печати цикла чтения-оценки-печати. Он также хранится в куче, так что вы можете позже получить к нему доступ через *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 (таким образом, заставляя анализировать весь файл), возникает огромная временная задержка. Если я дозирую по частям структуры, это происходит очень быстро.

person noisesmith    schedule 08.08.2013
comment
Спасибо за ответ. Я понимаю смысл ленивых вычислений, но в моем случае вызов (time (-> n :content count)) также приведет к ошибке java.lang.OutOfMemoryError: Java heap space. В целом, я пытаюсь найти способ получить более 50 МБ кучи, но не могу понять. - person Nicolas M.; 08.08.2013
comment
Сообщение об ошибке Несовместимые минимальные и максимальные размеры кучи указывают мне, что где-то установлен низкий максимум, который вам каким-то образом нужно обойти или переопределить. Параметры, вызвавшие эту ошибку, указали начальный размер кучи (-Xms), но не максимальный размер кучи (-Xmx). - person noisesmith; 08.08.2013
comment
Кроме того, я не знаю, что вы пытаетесь сделать, часто будет какой-то подход к сокращению, который выполняет то, что вы хотите, без необходимости одновременного хранения всего набора данных в памяти. - person noisesmith; 08.08.2013
comment
Я играю с данными в этот момент. Цель состоит в том, чтобы извлечь его из XML в структурированную базу данных с соответствующей настройкой ассоциаций. Когда я указываю как минимальную кучу, так и максимальную кучу, я вижу аналогичную ошибку. Знаете ли вы, где проверить конфигурацию размера кучи по умолчанию? Любая идея, есть ли способ узнать размер кучи из lein repl? Большое спасибо за твою помощь! - person Nicolas M.; 08.08.2013
comment
(.maxMemory (java.lang.Runtime/getRuntime)) покажет максимальный доступный объем памяти, также доступен .totalMemory и т. д. docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html - person noisesmith; 08.08.2013
comment
Спасибо! Оба возвращают 1060372480, это должно быть 1 ГБ. Мне непонятно, почему при разборе 50-мегабайтного файла не хватило памяти. :) - person Nicolas M.; 09.08.2013
comment
Структура данных, созданная xml/parse, в сотни раз превышает размер xml, из которого она исходит? Кажется маловероятным. - person noisesmith; 09.08.2013
comment
Я согласен. Я считаю, что размер кучи отличается от того, что возвращает maxMemory/totalMemory. Простой вызов (диапазон 1500000) — массив из 1,5 млн целых чисел — также возвращает ошибку Java-кучи OutOfMemoryError. - person Nicolas M.; 09.08.2013

Я не так много знаю о lein, но в mvn вы можете сделать следующее:

mvn  -Dclojure.vmargs="-d64 -Xmx2G" clojure:nrepl

(Я не думаю, что это имеет значение, но я всегда видел это с заглавной буквы G, это чувствительно к регистру?)

Вытягивание 100 МБ данных в память не должно быть проблемой. Я регулярно направляю данные объемом в ГБ через свои проекты.

Я всегда использую сервер 64-битной версии и для больших куч, и, кажется, это то, что они делают здесь:

Параметры JVM с использованием Leiningen

Я думаю, что большая проблема заключается в том, что, как вы это написали, это может оцениваться во время компиляции. Вам нужно обернуть этот вызов в функцию и отложить его выполнение. Я думаю, что компилятор пытается прочитать этот файл, а это, скорее всего, не то, что вам нужно. Я знаю, что с mvn вы получаете разные настройки памяти для компиляции и запуска, и вы тоже можете это получить.

person DrLivingston    schedule 06.12.2013