在Clojure中处理大文件时出现OutOfMemory错误

awh*_*awh 5 clojure out-of-memory

我正在关注algo-class.org课程,其中一个编程作业提供了一个格式如下的文件:

1 2
1 5
2 535
Run Code Online (Sandbox Code Playgroud)

...

有超过500万这样的行,我想在文件中读取并将其转换为整数向量的向量,如下所示:[[1 2] [1 5] [2 535] ...].

(defn to-int-vector [s]
    (vec (map #(Integer/parseInt %) (re-seq #"\w+" s))))    

(def ints (with-open [rdr (clojure.java.io/reader "<file>")]
               (doall (map to-int-vector (line-seq rdr)))))
Run Code Online (Sandbox Code Playgroud)

所以我相信这样,我不是将整个文件保存在内存中,而只生成一个大的整数向量.但是我从中得到了OutOfMemoryError.我尝试通过运行rand-int来生成相同大小和相同格式的向量,并且工作正常.

看起来内存问题是由生成的临时对象引起的?clojure处理这样一个案例的理想方式是什么?

更新:

是的,我意识到我持有整个整数向量.我已经提高了堆大小,现在可以了.我很感兴趣的是,一个载体和500万个元素(1000万个整数)可以占用大量内存 - 我必须为jvm分配3g.有没有其他方法可以记住内存?

Mar*_*nik 5

你不会相信实现的懒惰seq强加了多少开销.我在64位操作系统上测试了它:它类似于120字节.这对于每个懒惰的seq成员来说都是纯粹的开销.另一方面,向量具有相当低的开销,并且在给定足够大的向量的情况下基本上与Java数组相同.因此,尝试更换doallvec.

让我们看看你花了多少内存而没有开销.你有5e6对整数 - 这是5e6 x 8 = 40 MB.您可以通过使用short来节省并节省50%(我重复一遍 - 这不计算父集合的开销,并且持有该对的每个向量实例都有自己的开销).

保存的下一步是为外部集合和对使用原始数组.它仍然是一个非常实用的解决方案,因为阵列是可选的并且与语言很好地集成.要做到这一点,你只需要更换的两次出现vecto-array.

UPDATE

之间的区别Integer,并Short没有那么大,由于两者仍然完全成熟的对象.使用short-array(或int-array)而不是将数字对存储为原始数组可以节省更多to-array.


Art*_*ldt 1

in将确保整个结果存储在内存中 ,该内存至少与文件一样大,因为每行上的数字都存储在一个集合中,在本例中是一个 vec,它也占用空间def(def ints

另外默认情况下java会拒绝使用计算机中的所有内存,你可能需要设置maxHeapSize参数。

如果您从一个新的 repl 开始(尚未保存任何大列表),您是否仍然会耗尽内存?