2015-03-03 81 views
4

我很惊讶,这会引发内存不足错误,因为操作是在scala.collection.Iterator的顶部。各条线的尺寸小(< 1KB)令人惊讶的Scala迭代器“内存不足”错误

Source.fromFile("largefile.txt").getLines.map(_.size).max 

似乎它试图加载整个文件在存储器中。不知道哪一步触发了这一点。对于这样的基本操作来说,这是令人失望的行为。有没有简单的方法呢。这是由图书馆实施者设计的原因吗?

在Java8中尝试相同。

Files.lines(Paths.get("largefile.txt")).map(it -> it.length()).max(Integer::max).get 
//result: 3131 

而且这个工作可以预测。 Files.lines返回java.util.stream.Stream并且堆不会爆炸。

更新:看起来像归结为新的线解释。两个文件都被解释为UTF-8,并且它们都调用java.io.BufferedReader.readLine()。所以,仍然需要找出差异在哪里。我将两个片段主要类编译到同一个项目jar中。

+0

很多...文件大小比堆大小大。那不是重点。我认为这不重要,因为它是一个迭代器。 – smartnut007 2015-03-03 02:29:59

+3

重现这个文件有多大?我只是在10 GB上运行,没有任何问题。 – 2015-03-03 03:43:14

+5

你确定*每行少于1KB?如果没有任何换行符,那么调用'_.size'将会生成一个非常大的'String'并且很快耗尽内存。 – 2015-03-03 03:53:10

回答

3

我愿意成为的问题是,你要计数的'线'不同于getLines。从API:

(getLines)返回谁返回线(不包括换行字符 (S))的迭代器。它会将\ r \ n,\ r或\ n当作行 分隔符(最长匹配) - 如果您需要更精细的行为,可以直接使用 子类Source#LineIterator。

尝试在问题执行此对文件:

Source.fromFile("testfile.txt").getLines(). 
    zipWithIndex.map{ case(s, i) => (s.length, i)}. 
     foreach(e=> if (e._1 > 1000) println(
     "line: " + e._2 + " is: " + e._1 + " bytes!")) 

这会告诉你许多文件中的行如何大于1K的,什么指数是违规行。

+0

- 如果在打印任何东西前发生这种情况 - 您可以轻松地重构此行来打印/记录每一行的行号 - 这也将帮助您找到问题的索引。 – 2015-03-03 19:20:18

+0

根本没有解决我的问题。抱歉。 – smartnut007 2015-03-04 02:25:51

+1

@ smartnut007 - 是的,它确实如此。你假设你没有犯错,但没有显示你是如何得出这个结论的。我,Ben Reich,m-z和Aleksey Izmailov都表达了基本相同的观点 - 这个问题可能是程序员的错误。因此,你为什么不告诉我们它不是?当你运行上面的代码时,你会得到什么输出?您对Ben Reich的评论是什么回应,他无法用10GB文件重现您的结果? – 2015-03-04 16:23:10