【问题标题】:Clojure Leining REPL OutOfMemoryError Java heap spaceClojure Leining REPL OutOfMemoryError Java 堆空间
【发布时间】:2013-08-08 07:00:26
【问题描述】:

我正在尝试解析一个相当小的 (

(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)

这是我的 project.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"])

我尝试在我的 .bash_profile 中设置 LEIN_JVM_OPTS 和 JVM_OPTS,但没有成功。

当我尝试以下 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 repl 的堆大小吗?

谢谢。

【问题讨论】:

  • 是否正在将一些数据(XML 解析结果)存储在数组中?如果是,它有多大?
  • 你是在调用 REPL 的第二行吗?
  • Chiron:尚未将 XML 存储在任何数据结构中。只需像我的帖子中那样调用 parse 方法。 Igrapenthin:是的,我正在调用 REPL 中的解析行。该文件为 50MB,已解压缩。
  • 正如我在回答中所指出的,所有在 repl 顶层返回的东西都被存储(并且即使它们会变得懒惰也被完全评估),首先存储为 *1,然后存储为 *2等。

标签: xml-parsing clojure out-of-memory clojure-java-interop


【解决方案1】:

作为 Read-Eval-Print-Loop 的打印步骤的结果,在 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 的内容计数(从而强制解析整个文件)时,才会出现巨大的时间延迟。如果我对结构的各个子部分进行处理,这会很快发生。

【讨论】:

  • 感谢您的回答。我理解惰性评估的意义,但在我的情况下,调用 (time (-> n :content count)) 也会导致 java.lang.OutOfMemoryError: Java heap space 错误。总的来说,我正在尝试找到一种方法来获得超过 50MB 的堆,但无法弄清楚。
  • 错误消息“指定的最小和最大堆大小不兼容”向我表明在某处设置了较低的最大值,您需要以某种方式回避或覆盖。给您该错误的选项指定了起始堆大小 (-Xms) 但没有最大堆大小 (-Xmx)
  • 另外,我不知道您要做什么,通常会有一些缩减方法可以完成您想要的操作,而无需一次将整个数据集存储在内存中。
  • 此时我正在处理数据。目标是将其从 XML 提取到具有适当关联设置的结构化数据库中。当我同时指定最小堆和最大堆时,我看到了类似的错误。您知道在哪里检查默认堆大小配置吗?知道是否有办法从 lein repl 中知道堆大小?非常感谢您的帮助!
  • (.maxMemory (java.lang.Runtime/getRuntime)) 将显示可用的最大内存,.totalMemory 也可用等。docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html
【解决方案2】:

我不太了解 lein,但在 mvn 中,您可以执行以下操作:

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

(我认为这并不重要,但我总是看到它带有大写字母 G 是否区分大小写?)

将 100MB 的数据拉入内存应该没问题。我经常通过我的项目路由价值 GB 的数据。

我也总是使用 64 位版本的服务器来处理大堆,这似乎是他们在这里所做的:

JVM options using Leiningen

我认为更大的问题是,正如您所写的那样,这可能会在编译时进行评估。您需要将该调用包装在一个函数中,并推迟它的执行。我认为编译器正在尝试读取该文件,这可能不是您想要的。我知道使用 mvn 时,您可以在编译和运行时获得不同的内存设置,而且您可能也会得到。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-25
    • 2012-03-18
    • 2018-06-09
    相关资源
    最近更新 更多