5

我有一个非常大的数字列表,经过大量的数学运算。我只关心最后的结果。要模拟这种行为,请参见下面的示例代码:

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 (即没有可变变量或 ListBuffers )以某种方式对其进行操作,也许使用范围来强制 gc ?如果是这样,如何?谢谢

4

3 回答 3

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))

y它应该有助于避免为和创建中间完整结构z

于 2011-11-25T19:47:59.560 回答
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, ...
于 2011-11-25T19:55:07.227 回答
0

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

例如

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

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

于 2011-11-25T19:40:17.557 回答