2012-04-11 78 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表單中使用它們。這些細節確保已經處理的序列部分可以被垃圾收集,並且文件流在整個處理過程中保持打開狀態。