2012-06-01 73 views
5

這需要1秒左右爲什麼Scala並行集合有時會導致OutOfMemoryError?

(1 to 1000000).map(_+3) 

雖然這爲java.lang.OutOfMemoryError:Java堆空間

(1 to 1000000).par.map(_+3) 

編輯:

我有標準2.9.2階組態。我在scala提示符下輸入這個。在bash中我可以看到[-n「$ JAVA_OPTS」] || JAVA_OPTS =「 - Xmx256M -Xms32M」

而我沒有在我的環境中設置JAVA_OPTS。

百萬整數= 8MB, 創建列表兩次= 16MB

+0

你可以在這裏添加一些細節嗎?你有多少內存啓動你的JVM,還有其他交換機?你在REPL中運行它還是編譯它?如果你從REPL運行這個命令,你之前執行過多少條指令?我只是在我的REPL中嘗試過,並且在沒有OOM的情況下執行了它10次。 – drexin

+0

在我的REPEL中工作得很好。 – Jan

+0

@Jan對我來說,它在REPL中工作得很好,但只是第一次... – Christian

回答

9

它似乎確實涉及到JVM內存選項對股票一Parralel收集所需的內存。例如:

scala> (1 to 1000000).par.map(_+3) 

結束了一個OutOfMemoryError我第三次試圖對其進行評估,而

scala> (1 to 1000000).par.map(_+3).seq 

從來沒有失敗過。這個問題不是計算它的存儲的平行集合。

3

失敗有幾個原因:

  1. 並行收集不專業,所以對象將會被裝箱。這意味着你不能用8來乘以元素的數量來獲得內存使用。
  2. 使用map表示範圍轉換爲矢量。對於並行向量,還沒有實現有效的級聯,所以由不同處理器產生的合併中間向量通過複製而進行 - 需要更多的內存。這將在未來的版本中解決。
  3. REPL存儲先前的結果 - 每行中評估的對象保留在內存中。
+0

有一天有可能讓它們專業化嗎? –

+0

一旦數組專業化實施,一些集合類型應該是專門化的,是的。 – axel22

2

這裏有兩個問題,即存儲並行集合所需的內存量以及「通過」並行集合所需的內存量。

的差異可以看出這兩條線之間:

(1 to 1000000).map(_+3).toList 
(1 to 1000000).par.map(_+3).toList 

的REPL存儲所計算的表達式,記住。在我的REPL上,我可以在耗盡內存之前執行這7次。通過並行執行傳遞會臨時使用額外的內存,但一旦執行toList,那麼額外的使用就會被垃圾收集。

(1 to 100000).par.map(_+3) 

返回一個ParSeq [Int](在這個例子中是一個ParVector),它比正常的Vector佔用更多的空間。這一個我可以在內存不足之前執行4次,而我可以執行此操作:

(1 to 100000).map(_+3) 

在我耗盡內存之前11次。所以平行收集,如果你留着它們將佔用更多的空間。

作爲一種解決方法,您可以在返回它們之前將它們轉換爲簡單的集合,如List

至於爲什麼這麼多空間被平行集合佔用,爲什麼它仍然引用這麼多的東西,我不知道,但我懷疑views [*],如果你認爲這是一個問題,raise an issue for it

[*]沒有任何實際證據。

0

我有同樣的,但使用線程池似乎擺脫這個問題對我來說:

val threadPool = Executors.newFixedThreadPool(4) 
    val quadsMinPar = quadsMin.par 
    quadsMinPar.tasksupport = new ThreadPoolTaskSupport(threadPool.asInstanceOf[ThreadPoolExecutor]) 

ForkJoin非常大的集合可能創建太多的線程。

相關問題