2014-03-01 21 views
4

當我有這個功能能重現我的問題:的OutOfMemoryError使用塞克功能

(defn my-problem 
    [preprocess count print-freq] 
    (doseq [x (preprocess (range 0 count))] 
    (when (= 0 (mod x print-freq)) 
     (println x)))) 

一切工作正常,當我和身份功能這樣稱呼它:

(my-problem identity 10000000 200000) 
;it prints 200000,400000 ... 9800000 just as it should 

當我把它與seque函數我得到OutOfMemoryError:

(my-problem #(seque 5 %) 10000000 200000) 
;it prints numbers up to 2000000 and then it throws OutOfMemoryException 

我的理解是seq ue函數應該使用最大大小爲5的ConcurrentBlockingQueue將處理拆分爲兩個線程(在這種情況下)。我不明白內存泄漏的位置。

回答

5

方式seque實現,如果你更快地消耗元素比你能生產它們,大量的代理任務將堆積在由seque內部使用的隊列(高達序列中每個元素一個任務) 。從理論上講,你所做的事情應該沒問題,但實際上它並沒有真正實現。您只需運行即可看到相同的效果。

您還可以在flatland/useful中使用sequeue中的函數,該函數與clojure.core中的函數進行了不同的折衷。仔細閱讀文檔字符串,但我認爲它適用於您的情況。

+0

我很困惑這個答案。我從來沒有太多的理由去研究seque,但我認爲這個想法是,如果消費者領先於製片人,而不是堆積請求,他會被阻止。對我來說,內存錯誤對於實現的巨大範圍來說似乎更合理(引用在seque中關閉)。 –

+0

這就是這個想法,但是如果你仔細閱讀了實現(我花了很多時間來追蹤相關的錯誤),你會發現這確實發生了:['drain'](https:/ /github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L4971)每次消耗一個物品都會添加一個代理任務,而'fill'允許單個代理任務添加多個物品,如果隊列未滿。最終,可以有多個代理任務掛起,因爲輸入序列中有項目。 – amalloy

+0

@ A.Webb,如果你想要更有說服力的證據,試着運行'(dorun(seque(range)))',然後用'jmap -histo '來查看佔用所有堆空間的內容。這都是哈希映射和代理動作 - 很少有空間是懶惰的seq對象。 – amalloy