范围& Scala中的内存问题

时间:2011-11-25 19:30:45

标签: scala out-of-memory scoping

我有一个非常大的数字列表,经过大量的数学操作。我只关心最终的结果。要模拟此行为,请参阅下面的示例代码:

object X { 
def main(args:Array[String]) = {
    val N = 10000000
    val x = List(1 to N).flatten
    println(x.slice(0,10))
    Thread.sleep( 5000)
    val y = x.map(_*5)
    println(y.slice(0,10))
    Thread.sleep( 5000)
    val z = y.map( _+4)
    println(z.slice(0,10))
    Thread.sleep( 5000)
}
     }

所以x是一个非常大的列表。我只关心结果z。为了获得z,我首先必须在数学上操纵x来得到y。然后我操纵y得到z。 (我不能一步从x到z,因为操作非常复杂。这只是一个例子。)

因此,当我运行这个例子时,我的内存大概是因为x,y和z都在范围内而且它们都占用了内存。

所以我尝试以下方法:

def main(args:Array[String]) = {
    val N = 10000000
    val z = {
            val y = {
                val x = List(1 to N).flatten
                println(x.slice(0,10))
                Thread.sleep( 5000)
                x

            }.map(_*5)

            println(y.slice(0,10))
            Thread.sleep( 5000)
            y

    }.map( _+4)
    println(z.slice(0,10))
    Thread.sleep(5000)
}

所以现在只有z在范围内。因此可能会创建x和y,然后在它们超出范围时收集垃圾。但这不是发生的事情。相反,我再次耗尽内存!

(注意:我使用的是java -Xincgc,但没有帮助)

问题:当我只有1个大型列表有足够的内存时,我是否能以某种方式仅使用val来操作它(即没有可变的vars或ListBuffers),也许使用作用域强制gc?如果是这样,怎么样? 谢谢

3 个答案:

答案 0 :(得分:8)

你尝试过这样的事吗?

val N = 10000000
val x = List(1 to N).flatten.view // get a view
val y = x.map(_ * 5)
val z = y.map(_ + 4)
println(z.force.slice(0, 10))

它应该有助于避免为yz创建中间完整结构。

答案 1 :(得分:3)

使用view查看。它需要一个集合并且懒洋洋地加载它,只在需要时计算它。它不构成中间集合:

scala> (1 to 5000000).map(i => {i*i}).map(i=> {i*2}) .toList
java.lang.OutOfMemoryError: Java heap space
        at java.lang.Integer.valueOf(Integer.java:625)
        at scala.runtime.BoxesRunTime.boxToInteger(Unknown Source)
        at scala.collection.immutable.Range.foreach(Range.scala:75)
        at scala.collection.TraversableLike$class.map(TraversableLike.scala:194)
        at scala.collection.immutable.Range.map(Range.scala:43)
        at .<init>(<console>:8)
        at .<clinit>(<console>)
        at .<init>(<console>:11)
        at .<clinit>(<console>)
        at $print(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
        at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
        at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
        at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
        at java.lang.Thread.run(Thread.java:662)
scala> (1 to 5000000).view.map(i => {i*i}).view.map(i=> {i*2}) .toList
res10: List[Int] = List(2, 8, 18, 32, 50, 72, ...

答案 2 :(得分:0)

这是一个便宜的答案,但是你尝试用更多内存启动jvm吗?

e.g。

$ java -X     ...     -Xmx设置最大Java堆大小

此外,GC可能无济于事,因为听起来你在转换过程中同时在内存中遇到了两个列表,并且它们都被引用了。