我有一个非常大的数字列表,经过大量的数学操作。我只关心最终的结果。要模拟此行为,请参阅下面的示例代码:
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?如果是这样,怎么样? 谢谢
答案 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))
它应该有助于避免为y
和z
创建中间完整结构。
答案 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可能无济于事,因为听起来你在转换过程中同时在内存中遇到了两个列表,并且它们都被引用了。