使用Clojure Tranducers解析大文件:OutOfMemory Error

时间:2016-10-22 14:11:51

标签: clojure transducer

我想解析一个大的json文件(3GB)并返回此文件中每一行的哈希映射。我的直觉是使用换能器逐行处理文件并构造带有一些选定字段的矢量(文件中大约5%的字节)。

但是,以下代码抛出OutOfMemory异常:

file.json

{"experiments": {"results": ...}}
{"experiments": {"results": ...}}
{"experiments": {"results": ...}}

parser.clj

(defn load-with!
  "Load a file using a parser, a structure and a transducer."
  [parser structure xform path]
  (with-open [r (clojure.java.io/reader path)]
    (into structure xform (parser r))))

(def xf (map #(get-in % ["experiments" "results"])))
(def parser (comp (partial map cheshire.core/parse-string) line-seq))

(load-with! parser (vector) xf "file.json")

当我使用JVisualVM可视化进程时,堆会随着时间的推移而增长,并且在进程崩溃之前会超过25 GB。

在这种情况下换能器是否合适?有更好的选择吗?

我要求在函数结束时返回新结构。因此,我不能使用doseq来就地处理文件。

此外,我需要根据文件格式更改解析器和传感器。

谢谢!

1 个答案:

答案 0 :(得分:1)

你非常接近。我不知道.compact做了什么,但如果它与here中的json/parse-string相同,则此代码应该是您尝试在那里执行的操作。

看起来你想要这样的事情:

json/read-str

我猜这些只是将所有业务细节都删除到这里的最小例子中所犯的错误。使用下面的代码,我能够处理一个大文件,上面的代码给了我一个OOM错误:

(require '[clojure.data.json :as json])
(require '[clojure.java.io :as java])

(defn load-with!
  "Load a file using a parser, a structure and a transducer."
  [parser structure xform path]
  (with-open [r (java/reader path)]
    (into structure (xform (parser r)))))

(def xf (partial map #(get-in % ["experiments" "results"])))

(def parser (comp (partial map json/read-str) line-seq))


(load-with! parser [] xf "file.json")