为什么Scala并行集合有时会导致OutOfMemoryError?

时间:2012-06-01 09:20:35

标签: scala collections parallel-processing

大约需要1秒

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

虽然这给出了 java.lang.OutOfMemoryError:Java堆空间

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

修改

我有标准的scala 2.9.2配置。我在scala提示符下键入此内容。在bash中我可以看到[-n“$ JAVA_OPTS”] || JAVA_OPTS =“ - Xmx256M -Xms32M”

我没有在我的环境中设置JAVA_OPTS。

100万个整数= 8MB, 创建列表两次= 16MB

4 个答案:

答案 0 :(得分:9)

似乎肯定与存储Parralel集合所需的内存的JVM内存选项有关。例如:

scala> (1 to 1000000).par.map(_+3)
第三次尝试评估时,

最终得到OutOfMemoryError,而

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

从未失败过。问题不在于计算它是Parrallel集合的存储。

答案 1 :(得分:3)

失败的几个原因:

  1. 并行集合不是专门的,因此对象被装箱。这意味着您不能将元素数乘以8以获得内存使用量。
  2. 使用map表示将范围转换为矢量。对于并行向量,尚未实现有效的级联,因此合并由不同处理器产生的中间向量通过复制来进行 - 需要更多的存储器。这将在以后的版本中解决。
  3. REPL存储以前的结果 - 每行中评估的对象保留在内存中。

答案 2 :(得分: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

[*]没有任何真实证据。

答案 3 :(得分:0)

我也一样,但是使用ThreadPool似乎为我解决了这个问题:

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

大型集合的ForkJoin可能会创建太多线程。