Scala Infinite Iterator OutOfMemory - PullRequest
       12

Scala Infinite Iterator OutOfMemory

4 голосов
/ 27 декабря 2011

Я играю с ленивыми итераторами Scala и столкнулся с проблемой.Я пытаюсь прочитать большой файл, выполнить преобразование, а затем записать результат:

object FileProcessor {
  def main(args: Array[String]) {
    val inSource = Source.fromFile("in.txt")
    val outSource = new PrintWriter("out.txt")

    try {
      // this "basic" lazy iterator works fine
      // val iterator = inSource.getLines

      // ...but this one, which incorporates my process method, 
      // throws OutOfMemoryExceptions
      val iterator = process(inSource.getLines.toSeq).iterator

      while(iterator.hasNext) outSource.println(iterator.next)

    } finally {
      inSource.close()
      outSource.close()
    }
  }

  // processing in this case just means upper-cases every line
  private def process(contents: Seq[String]) = contents.map(_.toUpperCase)
}

Итак, я получаю исключение OutOfMemoryException для больших файлов.Я знаю, что вы можете столкнуться с ленивыми потоками Скалы, если будете хранить ссылки на голову потока.Поэтому в этом случае я стараюсь преобразовать результат process () в итератор и выбросить Seq, который он первоначально возвращает.

Кто-нибудь знает, почему это все еще вызывает потребление памяти O (n)?Спасибо!

Обновление

В ответ на fge и huynhjl кажется, что Seq может быть виновником, но я не знаю почему.Как пример, следующий код работает нормально (и я использую Seq везде).Этот код не создает исключение OutOfMemoryException:

object FileReader {
  def main(args: Array[String]) {

  val inSource = Source.fromFile("in.txt")
  val outSource = new PrintWriter("out.txt")
  try {
    writeToFile(outSource, process(inSource.getLines.toSeq))
  } finally {
    inSource.close()
    outSource.close()
  }
}

@scala.annotation.tailrec
private def writeToFile(outSource: PrintWriter, contents: Seq[String]) {
  if (! contents.isEmpty) {
    outSource.println(contents.head)
    writeToFile(outSource, contents.tail)
  }
}

private def process(contents: Seq[String]) = contents.map(_.toUpperCase)

1 Ответ

6 голосов
/ 27 декабря 2011

Как подсказал fge , измените process, чтобы взять итератор, и удалите .toSeq.inSource.getLines уже является итератором.

Преобразование в Seq приведет к запоминанию элементов.Я думаю, что он преобразует итератор в Stream и вызывает запоминание всех элементов.

Редактировать: Хорошо, это более тонкий.Вы делаете эквивалент Iterator.toSeq.iterator, вызывая iterator для результата процесса.Это может вызвать исключение нехватки памяти.

scala> Iterator.continually(1).toSeq.iterator.take(300*1024*1024).size
java.lang.OutOfMemoryError: Java heap space

Это может быть та же проблема, о которой сообщалось здесь: https://issues.scala -lang.org / browse / SI-4835 .Обратите внимание на мой комментарий в конце ошибки, это из личного опыта.

...