2011-11-25 9 views
5

Tengo una lista muy grande de números, que se someten a mucha manipulación matemática. Solo me importa el resultado final. Para simular este comportamiento, vea mi código de ejemplo a continuación:Problemas de alcance y memoria en Scala

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

So x es una lista muy grande. Solo me importa el resultado z. Para obtener z, primero tengo que manipular matemáticamente x para obtener y. Luego manipulo y para obtener z. (No puedo pasar de x a z en un solo paso, porque las manipulaciones son bastante complicadas. Esto es solo un ejemplo.)

Así que cuando ejecuto este ejemplo, me quedo sin memoria, presumiblemente porque x, y y z son todo en el alcance y todos ocupan memoria.

Así que pruebe lo siguiente:

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

Así que ahora sólo z es en su alcance. Así que, presumiblemente, se crean xyy y luego se recolectan los desechos cuando salen del alcance. Pero esto no es lo que sucede. En cambio, ¡nuevamente me quedo sin memoria!

(Nota: Estoy usando Java -Xincgc, pero no ayuda)

. Pregunta: Cuando tengo una memoria adecuada por sólo 1 lista grande, ¿hay algún modo manipularlo utilizando sólo de val (es decir, sin mutables vars o ListBuffers), ¿tal vez usar scoping para forzar gc? Si es así, cómo ? Gracias

+0

Siempre se necesita memoria para dos listas. Por curiosidad, ¿has configurado tu montón de Java? Considerado 'Array's? –

+0

Es cierto, siempre necesitaré memoria para 2 Listas, que tengo. Pero no debería necesitar memoria para 3 listas, que no tengo. ¿Estás de acuerdo? En cualquier caso, dado que xey están fuera del alcance, ¿por qué no se recogen basura una vez que la máquina virtual se da cuenta de que tiene poca memoria y las variables no están dentro del alcance? –

Respuesta

8

¿Has probado algo como esto?

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

Debe ayudar a evitar la creación de la estructura completa intermedio para y y z.

+1

¡Hola, gracias! Eso realmente soluciona el problema muy bien !! Sin errores de memoria. Tengo que incluir una "fuerza" en la última operación, pero parece que puedo hacer cualquier cantidad de operaciones intermedias en la vista sin asignar más memoria. Exactamente lo que quería. –

0

Es una respuesta barata, pero ¿intentó arrancar el jvm con más memoria?

p. Ej.

$ java -X ... -Xmx establecer el máximo tamaño de almacenamiento dinámico de Java

Además, GC, probablemente no ayudará, porque suena como que está consiguiendo cogido con dos listas en la memoria al mismo tiempo durante la transición, y ambos son referenciados.

3

Mire usando view. Toma una colección y la carga lentamente, solo calcula el valor cuando sea necesario. No forma una colección intermedia:

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, ...