csv - Clojure - 解析小型 CSV 文件的内存使用情况

标签 csv memory clojure garbage-collection

我正在尝试解析一个 50MB 的 CSV 文件。 ~2500 行,~5500 列,一列是字符串(日期为 yyyy-mm-dd),其余是带有很多空点的浮点数。我需要能够访问所有数据,所以想要实现完整的文件,这在那个大小下应该是可能的。

我尝试了以下几个选项:
(with-open [rdr (io/reader path)] (doall (csv/read-csv rdr))))
使用 line-seq 稍微手动一点的方法并手动将字符串解析为数字。

我在单个 slurp 上的 JVM 使用情况增加 100MB,文件大小的 2 倍。在解析数据时,我会根据它的完成方式增加 1-2GB。如果我多次打开文件并将其解析为同一个变量,内存使用量会不断增加,最终会出现内存错误并且程序失败。 (我知道查看任务管理器并不是查看内存泄漏的最佳方法,但事实是程序失败了所以某处存在泄漏)

打开文件的正确方法是什么?我的最后一个用例是我每天都会获取一个新文件,我希望服务器应用程序每天打开文件并处理数据,而不会耗尽内存并需要重新启动服务器。

编辑:为了比较,使用 Python pandas 读取该文件将消耗大约 100MB 的内存,并且随后重新读取该文件不会继续增加内存使用量。

Edit2:这是一个使用本地原子尝试查看发生了什么的最小示例:

(defn parse-number [s] (if (= s "") nil (read-string s)))

(defn parse-line [line]
  (let [result (atom [])]
    (doseq [x (clojure.string/split line #",")]
      (swap! result conj (parse-number x)))
    @result))

(defn line-by-line-parser [file]
  (let [result (atom [])]
    (with-open [rdr (clojure.java.io/reader file)]
      (doseq [line (line-seq rdr)]
        (swap! result conj (parse-line line)))
      @result)))

;in the repl:
(def x (line-by-line-parser "C:\\temp\\history.csv")) ; memory goes up 1GB
(def x (line-by-line-parser "C:\\temp\\history.csv")) ; memory goes up an extra 1GB
; etc

非常感谢!

最佳答案

只要您不将解析的数据保留在任何 GC 根目录下(如 defmemoize 函数),上面显示的代码就不会泄漏。您可以通过将代码循环 100 次并查看是否出现 OOM(我不指望任何)来轻松证明这一点。话虽如此,您可以按照其他人的建议采取一些措施来缓解内存压力。

如果您想确切地知道内存在哪里,请选择类似 this 的分析器。深入了解它。

我对你的情况的预感只是 GC 压力(不是泄漏)。具体使用read-string ,远不止conj/atom .尝试更换 read-string使用更低级别的内容(例如 Integer/parse ),您应该会看到很大的不同。 conj另一方面,从持久数据结构的角度来看(Python 不使用)是非常高效的,但当然它永远不会击败原始数组(Python 使用)。 atom通常用于并发。在您的情况下,它可以替换为 transient (和 persistent! )但我不认为它会产生很大的不同。

更新 - 添加分配火焰图

  • 如您所见,read-string运行时使用了 70% 的内存分配

  • enter image description here

    关于csv - Clojure - 解析小型 CSV 文件的内存使用情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59250104/

    相关文章:

    java - java中文件的读取和上传

    excel - 如何浏览保存目录?

    regex - Tcl正则表达式检测方括号

    带有元数据的 Clojure 变量

    java - 如何在 Clojure/Java 中使用 SOAP?

    clojure - 如何将 `static final` 常量从 Java 类拉入 Clojure 命名空间?

    matlab - 从 textread 更改为 textscan MATLAB

    objective-c - 为什么没有为原始数据类型分配内存?

    c - C 中的有序链表导致内存错误?

    c - 返回前释放内存