首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >支持流处理的OutOfMemoryException

支持流处理的OutOfMemoryException
EN

Stack Overflow用户
提问于 2014-03-21 09:20:30
回答 3查看 100关注 0票数 1

我编写了一个函数,在给定Stream[Long]的情况下,它将过滤出大于4,000,000的数字。

代码语言:javascript
复制
  def filterLt4Mil(xs: Stream[Long]) = {
    @tailrec
    def go(xs: Stream[Long], acc: Stream[Long]): Stream[Long] = xs match {
      case Stream() => acc
      case a #:: as if(a < 4000000L) => go(as, acc :+ a)
      case a #:: as if(a > 4000000L) => go(as, acc)
    }
    go(xs, Stream[Long]())
  }

但是,当我传入一个范围从0到10,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000的OutOfMemoryException

代码语言:javascript
复制
scala> val x = Stream.range(0,10000000L)
x: scala.collection.immutable.Stream[Long] = Stream(0, ?)

scala> filterLt4Mil(x)
java.lang.OutOfMemoryError: GC overhead limit exceeded
        at scala.collection.immutable.List.toStream(List.scala:312)

因为filterLt4Mil使用了尾部调用优化,所以我的理解是堆栈不应该溢出。

但是,为什么会出现这种OutOfMemoryException?我怎样才能防止它呢?

EN

回答 3

Stack Overflow用户

发布于 2014-03-21 10:29:23

您的函数有一些问题。

您的go函数具有颠倒输出流中元素的效果,因为您将其追加到累加器的末尾。

此外,附加而不是预先附加的动作会导致整个累加器被物化到一个列表中,然后在迭代中的每个步骤中复制该列表。这就是导致OutOfMemoryError的原因,因为您的实现需要二次内存。

此外,由于您没有处理a == 4000000L的情况,所以您的match代码块并不是详尽的。

如果您想要做的就是过滤出大于4000000的元素,这就足够了:

代码语言:javascript
复制
xs filterNot {_ > 4000000L}
票数 0
EN

Stack Overflow用户

发布于 2014-03-21 10:47:23

好吧,已经有一个用于流的filter方法,但我猜您想自己来做。

Streams有严格的头部和懒惰的尾巴。调用Stream.range(0,10000000L)将只有0在内存中,因为其余的将延迟求值。

但是在您的filterLt4Mil方法中,您将迭代整个流,导致该流中的所有元素都被求值,从而导致从010000000L的所有数字都被存储在内存中……存储这样的数量可能会导致OutOfMemoryException

例如:x.forall(_ >= 0)也会导致OutOfMemoryExceptionforall还必须在满足_ >= 0的同时遍历整个流。

您可能希望延迟应用您的filter方法。请看一下源代码。可能会让你对它的工作原理有所了解:(http://harrah.github.io/browse/samples/library/scala/collection/immutable/Stream.scala.html)

代码语言:javascript
复制
override final def filter(p: A => Boolean): Stream[A] = {
  // optimization: drop leading prefix of elems for which f returns false
  var rest = this dropWhile (!p(_))
  if (rest.isEmpty) Stream.Empty
  else new Stream.Cons(rest.head, rest.tail filter p)
}

关于你的代码还有一个问题。因为它必须遍历整个流,所以您可以猜测无限流会发生什么。

票数 0
EN

Stack Overflow用户

发布于 2014-03-21 11:33:21

我认为你没有搞砸堆栈--看起来你的堆快用完了(为了确认,切换到使用Int(s)而不是Long(s))。scala编译器应该知道如何使用tail-recurse优化这个函数。

10m的长度是64m。如果你正在创建一个64m列表中的10m个实例,那么你就有麻烦了。

此外,您可能希望使用惰性流,而不仅仅是尾部递归,以便对此函数进行内存优化。64m本身并不是很难,所以这个优化可能不是必需的。

在我看来,您要么创建了XS参数的10m个副本,要么创建了ACC参数的10M个副本。

在我看来,xs:参数似乎很懒;但是您在函数外部保留了对它的引用,而streams在scala中是记忆器。

所以我敢打赌,这个参数就是你会得到数百万个副本的那个(即,scala编译器不知道要去掉的中间值)。另一个参数应该在每次调用时进行垃圾回收(或者,因为我们讨论的是尾递归,所以在内存列表中优化为1,64m长)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22548462

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档