Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure和Java堆空间中的大型文件错误_Clojure_Heap_Inputstream - Fatal编程技术网

Clojure和Java堆空间中的大型文件错误

Clojure和Java堆空间中的大型文件错误,clojure,heap,inputstream,Clojure,Heap,Inputstream,我之前在一个网站上发布过——这是一个287GB的XML,带有Wikipedia转储文件,我想把它放到CSV文件中(修订版、作者和时间戳)。我成功地做到了这一点。在我得到StackOverflow错误之前,但是在解决了第一个问题之后,我得到了:java.lang.OutOfMemoryError:java堆空间错误 我的代码(部分取自Justin Kramer的答案)如下所示: (defn process-pages [page] (let [title (article-titl

我之前在一个网站上发布过——这是一个287GB的XML,带有Wikipedia转储文件,我想把它放到CSV文件中(修订版、作者和时间戳)。我成功地做到了这一点。在我得到StackOverflow错误之前,但是在解决了第一个问题之后,我得到了:java.lang.OutOfMemoryError:java堆空间错误

我的代码(部分取自Justin Kramer的答案)如下所示:

(defn process-pages
  [page]
  (let [title     (article-title page)
        revisions (filter #(= :revision (:tag %)) (:content page))]
    (for [revision revisions]
      (let [user (revision-user revision)
            time (revision-timestamp revision)]
        (spit "files/data.csv"
              (str "\"" time "\";\"" user "\";\"" title "\"\n" )
              :append true)))))

(defn open-file
[file-name]
(let [rdr (BufferedReader. (FileReader. file-name))]
  (->> (:content (data.xml/parse rdr :coalescing false))
       (filter #(= :page (:tag %)))
       (map process-pages))))

我不显示
文章标题
修订用户
修订标题
函数,因为它们只是从页面或修订哈希中的特定位置获取数据。任何人都可以帮我-我对Clojure很陌生,不明白这个问题

不幸的是,
data.xml/parse
并不懒惰,它试图将整个文件读入内存,然后对其进行解析

相反,使用仅在ram中保存当前正在处理的部件的。然后,您需要重新构造代码,以便在读取输入时编写输出,而不是收集所有xml然后输出

你的线路

(:content (data.xml/parse rdr :coalescing false)
将所有xml加载到内存中,然后从内存中请求内容密钥。这会把事情搞砸的

懒散答案的大致轮廓如下:

(with-open [input (java.io.FileInputStream. "/tmp/foo.xml")
            output (java.io.FileInputStream. "/tmp/foo.csv"]
    (map #(write-to-file output %)
        (filter is-the-tag-i-want? (parse input))))
要有耐心,使用
(>数据ram)
总是需要时间:)

要清楚,
(:content(data.xml/parse rdr:coalescing false))
是懒惰的。检查它的等级,如果你不确定的话,拉第一个项目(它会立即返回)

也就是说,在处理大型序列时要注意两件事:抓住头部,以及未实现/嵌套的懒惰。我认为您的代码受到后者的影响

以下是我的建议:

1) 将
(dorun)
添加到
->
调用链的末尾。这将迫使在不抓住头部的情况下完全实现序列

2) 将
过程页面中
更改为
doseq
。你在向一个文件吐痰,这是一个副作用,你不想在这里懒洋洋地这样做

正如Arthur所建议的,您可能希望打开一个输出文件一次并继续写入,而不是为每个Wikipedia条目打开并写入(spit)

更新

这里有一个重写,试图更清楚地分离关注点:

(defn filter-tag [tag xml]
  (filter #(= tag (:tag %)) xml))

;; lazy
(defn revision-seq [xml]
  (for [page (filter-tag :page (:content xml))
        :let [title (article-title page)]
        revision (filter-tag :revision (:content page))
        :let [user (revision-user revision)
              time (revision-timestamp revision)]]
    [time user title]))

;; eager
(defn transform [in out]
  (with-open [r (io/input-stream in)
              w (io/writer out)]
    (binding [*out* out]
      (let [xml (data.xml/parse r :coalescing false)]
        (doseq [[time user title] (revision-seq xml)]
          (println (str "\"" time "\";\"" user "\";\"" title "\"\n")))))))

(transform "dump.xml" "data.csv")

我在这里没有看到任何会导致过度内存使用的东西。

我不知道Clojure,但在普通Java中,可以使用基于SAX事件的解析器,如
这不需要将XML加载到RAM中

他已经在使用contrib提供的
数据。正如您所指出的,它是懒惰的。对于刚接触Clojure的人来说,关于dorun的一点可以更清楚一些:问题中所示的open file函数返回调用处理页面的结果序列,当从repl调用函数时,打印序列会导致所有结果同时保存在内存中。对结果调用dorun会导致对序列的元素求值,并返回nil,这样就不需要同时将所有结果都存储在内存中。谢谢解释!我现在确实理解(希望如此)这个代码片段中的惰性是如何工作的,并改变了您的建议,但仍然
OutOfMemoryError:Java堆空间
。我正在处理最终文件的1GB样本,但它仍然会引发内存错误。非常感谢您的帮助。请参阅我的最新更新。若你们仍然摆脱记忆错误,我不知道为什么。我使用了与此非常相似的代码,没有内存问题。解决问题的思路:同一项上的内存是否总是不足?该项目是否与众不同(例如,非常大,修改很多)?您是否尝试过给JVM更多内存?你确定你没有在任何地方保留任何子字符串(JVM不包含仍在使用的子字符串的GC字符串)?基本上-谢谢你的帮助。A花了更多的时间在它上面,对我来说JVM的调整太复杂了,我尝试使用更多的内存选项,我得到的错误也更多。在我能够正确处理这个问题之前,我可能会花更多的时间在Clojure和JVM上。