加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 为什么Stream.filter内存不足?

发布时间:2020-12-16 18:47:55 所属栏目:安全 来源:网络整理
导读:这两个表达式应该是一样的: Stream.from(1).filter(_ 0).headStream.from(1).find(_ 0) 应该循环,直到它们返回Int.MinValue.这正是具有过滤器的版本所做的,但是在查找时会生成OutOfMemoryError.看看他们的实现,我无法弄清楚两个版本都不会产生OutOfMemoryEr
这两个表达式应该是一样的:

Stream.from(1).filter(_ < 0).head
Stream.from(1).find(_ < 0)

应该循环,直到它们返回Int.MinValue.这正是具有过滤器的版本所做的,但是在查找时会生成OutOfMemoryError.看看他们的实现,我无法弄清楚两个版本都不会产生OutOfMemoryError.

以下是Stream.filter的实现:

override def filter(p: A => Boolean): Stream[A] = {
  // optimization: drop leading prefix of elems for which f returns false
  // var rest = this dropWhile (!p(_)) - forget DRY principle - GC can't collect otherwise
  var rest = this
  while (!rest.isEmpty && !p(rest.head)) rest = rest.tail
  // private utility func to avoid `this` on stack (would be needed for the lazy arg)
  if (rest.nonEmpty) Stream.filteredTail(rest,p)
  else Stream.Empty
}

find继承自LinearSeqOptimized,具有以下定义:

override /*IterableLike*/
def find(p: A => Boolean): Option[A] = {
  var these = this
  while (!these.isEmpty) {
    if (p(these.head)) return Some(these.head)
    these = these.tail
  }
  None
}

它们都有一个while循环,它丢弃了不满足谓词的Stream元素.因为这应该保持对Stream开头的引用,所有这些创建的元素应该在内存中累积,直到我们用完空间.除非我真的误解了这里发生的事情,否则Stream.filter会在进入while循环之前以某种方式从堆栈帧中消除它.关于为什么不使用dropWhile的Stream.filter中的注释看起来像一个提示,但我不知道它指的是什么.

我的下一步是学习如何反汇编和读取JVM字节码,但我真的希望有人知道这里发生了什么.

解决方法

它是HotSpot和Scala特性实现方式的结合.

如果我使用-Xint关闭HotSpot,Stream.filter也会因OutOfMemoryException而死亡.在生成的字节码本身,这和变量休息,这些存储在不同的内存位置,但因为这只用于初始化这些变量,我相信HotSpot足够聪明,可以简单地重用内存位置.这解释了为什么Stream.filter不会耗尽内存.

HotSpot对Stream.filter的优化也应该适用于LinearSeqOptimized.find,但是由于实现了traits的方式,因此保留了对它的引用.当在特征内部实现方法时,Scala会将该方法编译为静态方法.当一个类继承该特征时,Scala会创建一个调用静态方法的小型存根方法.因此,即使HotSpot优化了LinearSeqOptimized.find的静态方法,存根方法的堆栈帧仍然具有对此的引用.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读