在scala中将文件解析为流时,如何避免内存不足

时间:2017-05-19 02:29:23

标签: scala

我是scala的新手,您想了解为什么会出现以下代码 在GC overhead limit exceeded中,应采取哪些措施来避免它。

import scala.io.Source
import scala.annotation.tailrec

  def getItems(file: Source): Stream[String] = {
    @tailrec
    def acc(it: Iterator[String],
            item: String,
            items: Stream[String]): Stream[String] = {

      if(it.hasNext){
        val line = it.next
        line take 1 match {
          case " " =>
            acc(it, item + "\n" + line, items)
          case "1" =>
            acc(it, item, Stream.cons(item, items))
        }
      }
      else {
        Stream.cons(item, items)
      }
    }
    acc(file.getLines(), "", Stream.Empty)
  }

3 个答案:

答案 0 :(得分:1)

您的代码有两个原因可能导致 OOM

  1. item将递归添加文件长度,这可能会非常大,具体取决于您的文件大小。
  2. 对于您的Stream重复附加已累积的itemStream,这也可能非常大,导致 OOM
  3. 有一种方法可以通过使用延迟评估Stream而不使用memorization来保存此方案。

答案 1 :(得分:0)

我试图弄清楚你实际上要做什么,但问题是你正在使用你的acc函数递归,直到你的输入文件没有更多的元素。这是一个非常简单的示例,它将迭代器转换为流。

def convert[T]( iter : Iterator[T] ) : Stream[T] = 
  if ( iter.hasNext ) {
    Stream.cons( iter.next, convert( iter ) )
  } else {
    Stream.empty
  }

此外,您将所有以空格开头的行追加到item。我不知道您在输入中有多少这样的行,但如果所有行都以空格开头,如果您的输入文件有n个字符,则会使用(n^2)/2个字符。但我不认为这就是你的递归失败的原因。

答案 2 :(得分:0)

scala中的

Stream实际上是一个漏洞抽象。它假装是Seq,但如果流量巨大,则不能将其用作常规集合。 这是一篇关于流http://blog.dmitryleskov.com/programming/scala/stream-hygiene-i-avoiding-memory-leaks/的文章 在您的情况下,违反了规则“不在方法参数中存储Streams”(items)。