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

trz*_*zek 6 heap inputstream clojure

我之前发布了一个巨大的XML文件 - 它是一个287GB的XML,带有维基百科转储我想要放入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))))
Run Code Online (Sandbox Code Playgroud)

我没有表现article-title,revision-userrevision-title功能,因为他们只是简单地从网页或修改散列特定场所需要的数据.任何人都可以帮助我 - 我在Clojure中真的很新,并没有遇到问题.

Jus*_*mer 4

需要明确的是,(:content (data.xml/parse rdr :coalescing false))我很懒。如果您不相信,请检查其类别或拉出第一个项目(它将立即返回)。

也就是说,在处理大型序列时需要注意以下几点:抓住头部,以及未实现/嵌套的惰性。我认为你的代码受到后者的影响。

这是我的建议:

1) 添加(dorun)到调用链的末尾->>。这将迫使序列完全实现,而无需抓住头部。

2)更改forprocess-page. doseq你正在向文件吐口水,这是一个副作用,你不想在这里懒惰地这样做。

正如 Arthur 建议的那样,您可能希望打开一个输出文件一次并继续写入它,而不是为每个维基百科条目打开并写入(吐出)。

更新

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

(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")
Run Code Online (Sandbox Code Playgroud)

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