Clojure OutOfMemoryError

时间:2011-11-12 06:29:55

标签: clojure out-of-memory

我正在阅读在大型序列上使用loop / recur时,懒惰序列如何导致OutOfMemoryError的问题。我正在尝试从内存加载一个3MB的文件来处理它,我想这发生在我身上。但是,我不知道是否有一种惯用的方法来解决它。我尝试输入doall,但是我的程序似乎没有终止。小投入工作:

小输入(文件内容):AAABBBCCC 正确输出:((65 65)(65 66)(66 66)(67 67)(67 67))

代码:

(def file-path "/Users/me/Desktop/temp/bob.txt")
;(def file-path "/Users/me/Downloads/3MB_song.m4a")

(def group-by-twos
  (fn [a-list]
    (let [first-two (fn [a-list] (list (take 2 a-list)))
          the-rest-after-two (fn [a-list] (rest (rest a-list)))
          only-two-left? (fn [a-list] (if (= (count a-list) 2) true false))]
      (loop [result '() rest-of-list a-list]
        (if (nil? rest-of-list)
          result
          (if (only-two-left? rest-of-list)
            (concat result (list rest-of-list))
            (recur (concat result (first-two rest-of-list))
                   (the-rest-after-two rest-of-list))))))))

(def get-the-file
  (fn [file-name-and-path]
   (let [the-file-pointer
           (new java.io.RandomAccessFile (new java.io.File file-name-and-path) "r")
         intermediate-array (byte-array (.length the-file-pointer))] ;reserve space for final length
      (.readFully the-file-pointer intermediate-array)
      (group-by-twos (seq intermediate-array)))))

(get-the-file file-path)

正如我上面所说,当我在一堆地方放入doalls时,它似乎没有完成。我怎样才能让它运行大文件,有没有办法摆脱做我需要做的事情的认知负担?一些规则?

3 个答案:

答案 0 :(得分:2)

你正在内存中完全读取文件,然后在这个字节数组上创建一个seq,它实际上没有给你任何延迟序列的好处,因为所有需要的数据已经加载到内存中,而惰性序列实际意味着生成/在需要时生成数据。

您可以使用以下内容创建文件内容的seq:

(def get-the-file
  (fn [file-name-and-path]
   (let [the-file-pointer
           (new java.io.RandomAccessFile (new java.io.File file-name-and-path) "r")
         file-len (.length the-file-pointer)] ;get file len
      (partition­ 2 (map (fn [_] (.readByte the-file-pointer)) (range file-len))))))

注意:我还没有真正尝试过,但我希望它能给你一个至少关于懒文件阅读部分的想法

答案 1 :(得分:2)

我想一个惯用的解决方案是:

 (partition 2 (map int (slurp "/Users/me/Desktop/temp/bob.txt")))

由于将完整文件加载到内存中,这并不完全是懒惰的,但对于不太大的文件,它应该没有问题。但是分区和地图是懒惰的,所以如果你用缓冲的阅读器替换slurp,你将得到一个完全懒惰的版本。

注意:如果文件大小为奇数,这将吞下最后一个字符。如果尺寸是奇数,目前还不清楚你的期望。如果您想在自己的列表中包含最后一个值,可以使用(partition 2 2 [] ... )

user=> (partition 2 (map int "ABCDE"))
((65 66) (67 68))
user=> (partition 2 2 [] (map int "ABCDE"))
((65 66) (67 68) (69))

答案 2 :(得分:1)

在处理大量数据时要小心clojure数据结构。 (典型的Clojure应用程序使用的内存是同一Java应用程序的两到三倍 - 序列内存很昂贵)。 如果您可以将整个数据读入数组,请执行此操作。然后处理它,同时确保不保留对任何序列头的引用,以确保在此过程中发生垃圾收集。

字符串也比char原语大得多。单个字符串是26个字节,char是2个字节。 即使您不喜欢使用数组,arraylist也比序列或向量小几倍。