2012-04-11 59 views
4
一个大文件

我正在关注的algo-class.org过程时,它的编程任务之一提供如下格式的文件:内存不足错误处理Clojure中

1 2 
1 5 
2 535 

...

有超过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))))) 

所以我相信这样,我没有把整个文件放在内存中,只生成一个大整数向量。但是我从这里得到OutOfMemoryError。我尝试通过运行rand-int生成相同大小和相同格式的矢量,并且工作正常。

看起来内存问题是由临时对象生成的? clojure处理这种情况的理想方式是什么?

更新:

是的,我知道我是保存整个整数向量。我已经提高了堆的大小,现在它可以工作。我感兴趣的是一个载体和500万个元素(1000万个整数)可以占用如此多的内存 - 我必须为jvm分配3g。有没有其他方式可以减少记忆?

回答

1

(def intsdef将确保整个结果被存储在存储器
因为在每行的数被储存在集合中 ,在这种情况下,VEC这将是至少作为文件一样大,即也占用空间。

也默认java会拒绝使用计算机中的所有内存,您可能需要设置maxHeapSize参数。

如果您从一个新的repl开始(尚未拥有任何大型列表),您是否仍然用完内存?

5

你不会相信实现的懒惰seq施加多少开销。我在64位操作系统上测试过它:它有点像120字节。对于每个懒惰的seq成员来说,这是纯粹的开销。另一方面,矢量具有相当低的开销,并且基本上与给定足够大的矢量的Java数组相同。因此请尝试用vec替换doall

我们还可以看看您有多少内存没有开销。你有5e6对整数 - 即5e6 x 8 = 40 MB。你可以通过使用短裤并节省50%(我重复---这不包括父集合的开销,每个持有该集合的向量实例都有自己的开销)。

保存的下一步是使用原始数组作为外部集合和对。它可能仍然是一个非常实用的解决方案,因为数组可以被选择并与语言很好地集成在一起。为此,您只需将vecto-array替换为两个。

UPDATE

IntegerShort之间的差别并不大,由于两者仍然完全成熟的对象。使用short-array(或int-array)而不是to-array将数字对存储为原始数组会节省更多。

+0

是的,vec和数组有助于减少内存。谢谢! – awh 2012-04-11 09:39:04

+0

更新了我的答案 - 尝试对数字对使用“int-array”或“short-array”而不是“to-array”。 – 2012-04-11 09:54:29

2

很难利用懒惰,并且同时封装with-open。在你的情况下,懒惰很重要,因为它可以在内存中只有行的“相关”部分或整型向量序列。

一个解决方案的问题是不封装with-open,并包含一个with-open形式的动态范围内的整条生产线,处理逻辑:这里

(with-open [rdr (clojure.java.io/reader "<file>")] 
    (doseq [int-vector (map to-int-vector (line-seq rdr))] 
    (process int-vector))) 

两个重要的细节是,你不保存行和int向量序列,并且只在with-open表单中使用它们。这些细节确保已经处理的序列部分可以被垃圾收集,并且文件流在整个处理过程中保持打开状态。