首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Clojure OutOfMemoryError

Clojure OutOfMemoryError
EN

Stack Overflow用户
提问于 2011-11-12 14:29:55
回答 3查看 435关注 0票数 1

我正在读一篇关于懒惰序列如何导致OutOfMemoryError的文章,比如说,在大序列上使用循环/递归。我试图从内存中加载一个3MB的文件来处理它,我想这正发生在我身上。但是,我不知道是否有一种惯用的方法来修复它。我试着放入doall's,但我的程序似乎没有终止。小输入可以工作:

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

代码:

代码语言:javascript
复制
(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)

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

EN

回答 3

Stack Overflow用户

发布于 2011-11-12 16:03:23

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

您可以使用如下命令在文件内容上创建seq:

代码语言:javascript
复制
(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))))))

注意:我还没有真正尝试过它,但我希望它至少能让您对延迟文件读取部分有所了解

票数 2
EN

Stack Overflow用户

发布于 2011-11-12 16:37:18

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

代码语言:javascript
复制
 (partition 2 (map int (slurp "/Users/me/Desktop/temp/bob.txt")))

这并不是完全懒惰,因为完整的文件被加载到内存中,但对于不太大的文件,它应该可以正常工作。然而,分区和映射是懒惰的,所以如果你用一个缓冲的读取器代替slurp,你将得到一个完全懒惰的版本。

注意:如果文件的大小是奇数,这将吞噬最后一个字符。如果大小是奇数,还不清楚你所期望的是什么。如果您想让最后一个值出现在它自己的列表中,可以使用(partition 2 2 [] ... )

代码语言:javascript
复制
user=> (partition 2 (map int "ABCDE"))
((65 66) (67 68))
user=> (partition 2 2 [] (map int "ABCDE"))
((65 66) (67 68) (69))
票数 2
EN

Stack Overflow用户

发布于 2011-11-14 01:29:47

在处理大量数据时,请注意clojure数据结构。(典型的Clojure应用程序使用的内存是相同Java应用程序的两到三倍-序列是内存昂贵的)。如果您可以将整个数据读取到一个数组中,就可以这样做。然后处理它,同时确保不保留对任何序列头部的引用,以确保垃圾收集在过程中发生。

此外,字符串比char基元大得多。单个字符字符串为26个字节,字符为2个字节。即使你不喜欢使用数组,arraylist也比序列或向量小好几倍。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8102972

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档