2013-02-21 62 views
8

this SO thread中,我瞭解到在大集合上保留對seq的引用將會阻止整個集合被垃圾收集。什麼時候應該避免在Clojure中使用`seq`?

首先,線程是從2009年開始這是在「現代」的Clojure(V1.4.0或V1.5.0)仍然如此?

其次,這個問題是否也適用於懶序列?例如,(def s (drop 999 (seq (range 1000))))會允許垃圾收集器退出序列的第一個999元素嗎?

最後,有沒有一個很好的解決大集合這個問題的方法?換句話說,如果我擁有一千萬個元素的矢量,我是否可以消耗這個矢量,使得所消耗的部分可以被垃圾收集?如果我有一個包含1000萬個元素的散列圖,那麼呢?

的原因,我問的是,我的工作相當大的數據集,而我不得不更加小心不要保留對象的引用,讓我不需要的對象可以被垃圾收集。事實上,在某些情況下,我遇到java.lang.OutOfMemoryError: GC overhead limit exceeded錯誤。

+0

我想@ cgrand的例子'(drop 999990(vec(range 1000000))''是由於介入向量和'subvec'toring的行爲。我不懷疑一個懶惰的「缺點」序列會這樣做。如果您需要在保留子向量的同時釋放向量,則可以將子向量'複製到新向量中。非常有趣的問題,但我還在等待看到答案! – 2013-02-21 17:03:48

回答

6

它始終是,如果你「守住頭」序列的那麼的Clojure將被迫把一切都在內存中的情況。它沒有選擇:你仍然保留對它的引用。

但是,「達到的GC開銷限制」與內存不足錯誤不一樣 - 更可能是您運行一個虛構工作負載的標誌,該工作負載創建和放棄對象的速度太快,以至於欺騙GC認爲它超載。

參見:

如果你把正在處理中的項目的實際工作量,我懷疑你會看到這個錯誤將不再發生。在這種情況下,您可以輕鬆處理大於可用內存的惰性序列。

像載體和包含HashMap混凝土藏品然而一個不同的問題:這些都不是懶惰的,所以必須始終完全在內存中舉行。如果您有內存更大的數據集,然後你的選項包括:

  • 使用延遲序列,不守住頭
  • 使用專門收藏支持懶加載(Datomic使用一些結構是這樣,我相信)
  • 對待數據作爲事件流(使用類似風暴)
  • 編寫自定義代碼將數據劃分成塊並處理它們一次一個。
+0

謝謝,mikera,這很有幫助。我期待看看Storm和Datomic。 – 2013-02-25 13:09:09

0

如果你持有一個序列的頭部具有約束力,那麼你是正確的,它不能被gc'd(這就是使用Clojure的每一個版本)。如果你正在處理大量的結果,你爲什麼需要保持頭部?

至於它的方式,是的! lazy-seq實現可以處理已經被「處理」且不在綁定內直接引用的部分。只要確保你不緊盯着序列的頭部。

+1

我認爲這個問題是,即使沒有與原始的綁定,一些子集合也可以引用整個原始的東西?例如,「subvec」的文檔讀取爲「...結果與最初的矢量共享結構並且不進行修整」。我認爲這個意思是'subvec'覆蓋了原文,並且重新設置了偏移量,但是我沒有深入到Clojure的內部。 – 2013-02-21 18:20:40

+0

@ A.Webb,我相信你是正確的,並且從上面的例子創建的子向量將保留集合的頭部。 (drop 999990(lazy-seq(range 1000000)))would not(至少從我的理解) – 2013-02-21 18:32:09

+0

關於在lazy-seq中包裝序列沒有什麼魔力 - 它只會給一個運行一次函數返回一個'list *'包裝在相同的位置。 (另外,'range'已經很懶。) – 2013-02-21 19:03:28

相關問題