我有一堆要并行处理的 xml 文件。我使用 future 的 scala 代码 (2.9.2) 开始时运行良好,但最终耗尽了我机器上 32G 内存的近 100%。当我按顺序执行此操作时不会发生这种情况,所以我猜测在使用 scala futures 时垃圾收集有问题。
这是我的代码的精简版。谁能告诉我哪里出了问题?
val filenameGroups = someStringListOfFilepaths.grouped(1000).toStream
val tasks = filenameGroups.map {
fg =>
scala.actors.Futures.future {
val parser = new nu.xom.Builder() // I'm using nu.xom. Not sure it matters.
fg.map {
path => {
val doc = parser.build(new java.io.File(path))
val result = doc.query(some xpath query)
result
}
}.toList
}
}
val pairs = tasks.par.flatMap(_.apply)
预计到达时间:好的,我解决了这个问题,但我仍然不知道为什么这会产生影响。
我提取了内部循环中的大部分代码,然后重新运行它。并从 future 中取出解析器实例化。内存使用率现在稳定在 17% 的不错水平。有人知道为什么这会有所作为吗?
这是我所做的简化版本:
def process(arglist...) = yada
val tasks = filenameGroups.map {
fg =>
val parser = new nu.xom.Builder()
scala.actors.Futures.future {
process(fg, parser)
}
}
val pairs = tasks.par.flatMap(_.apply)
最佳答案
Futures 无法真正预测您需要多少个线程或您的计算将占用多少内存,因此通常您有责任将适当的序列化计算放入适度数量的 futures 中。特别是,如果您使用的是 8 核机器,您可能不想分组比 someStringListOfFilepaths.length/8
小得多(如果您的文件太大以至于您不能拥有 8一次在内存中)。您可以使用检查核心数量的标准 Java 技巧,covered on SO和许多其他地方,如果你想在每台机器上扩展它而不必考虑它。 (在这种情况下,可能还需要检查 Runtime.getRuntime.maxMemory
,以防万一您使用的机器有很多内核但 RAM 不多(或者分配给 VM 的内存不多)。)
(顺便说一句,在您的最小示例中,既有懒惰又有 future ,但懒惰对您没有任何作用。 future 在创建时已经没有运行,因此延迟 future 的实例化可能对您没有任何帮助。 )
另请注意,如果您有 200k 个文件,最终将得到 200k 个结果,并且根据结果的大小,这可能会占用大量内存。可能不是 32G,但谁知道文件中有什么?
关于scala - scala future 的垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12921619/