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

标签 clojure heap inputstream

我之前在 huge XML file 上发过帖子- 这是一个带有维基百科转储的 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))))

我不显示 article-title , revision-userrevision-title函数,因为它们只是简单地从页面或修订哈希中的特定位置获取数据。任何人都可以帮我解决这个问题——我是 Clojure 的新手,没有遇到这个问题。

最佳答案

为了清楚起见,(:content (data.xml/parse rdr :coalescing false))是懒惰。如果您不相信,请检查它的类或拉出第一个项目(它会立即返回)。

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

这是我的建议:

1) 添加 (dorun)到最后->>调用链。这将迫使序列完全实现,而无需捕获头部。

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

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

更新 :

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

(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 - Clojure 中的巨大文件和 Java 堆空间错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9983983/

相关文章:

ubuntu - 在 ubuntu 服务器上使用插孔的实时音频流

java - 如何将这个 Java 正则表达式转换为 Go 的正则表达式语法?

java - 双向更新堆(Prim 算法)

java - 通过Key区分优先级队列堆的时间复杂度

java - 优先队列/堆更新

c++ - 输入流上的基于范围的循环

javascript - 网页开发: Building a graph and running an algorithm

Clojure 的 :require and Instaparse

java - 将 JSch 的标准 iostream 重定向到 primefaces 的终端

java - 简单的 Java 进程输入/输出失败