2012-09-20 46 views
6

我最近刚开始玩Scala,跑过以下内容。下面是4种不同的方式来遍历文件的行,做一些事情,并将结果写入另一个文件。其中一些方法按照我的想法工作(尽管使用大量内存来完成这些工作),并且有些方法不会记忆。Scala Iterable Memory Leaks

这个想法是将Scala的getLines Iterator封装为一个Iterable。我不在乎它是否多次读取文件 - 这正是我期望它做的。

这里是我的摄制代码:

class FileIterable(file: java.io.File) extends Iterable[String] { 
    override def iterator = io.Source.fromFile(file).getLines 
} 

// Iterator 

// Option 1: Direct iterator - holds at 100MB 
def lines = io.Source.fromFile(file).getLines 

// Option 2: Get iterator via method - holds at 100MB 
def lines = new FileIterable(file).iterator 

// Iterable 

// Option 3: TraversableOnce wrapper - holds at 2GB 
def lines = io.Source.fromFile(file).getLines.toIterable 

// Option 4: Iterable wrapper - leaks like a sieve 
def lines = new FileIterable(file) 

def values = lines 
     .drop(1) 
     //.map(l => l.split("\t")).map(l => l.reduceLeft(_ + "|" + _)) 
     //.filter(l => l.startsWith("*")) 

val writer = new java.io.PrintWriter(new File("out.tsv")) 
values.foreach(v => writer.println(v)) 
writer.close() 

它读取该文件是10GB〜带有1MB线。

前两个选项使用恒定数量的内存(〜100MB)迭代文件。这是我所期望的。这里的缺点是一个迭代器只能使用一次,它使用Scala的call-by-name约定作为伪代码。 (仅供参考,等效的c#代码使用〜14MB)

第三种方法调用TraverableOnce中定义的Iterable。这个工作,但它使用大约2GB做同样的工作。不知道内存在哪里,因为它无法缓存整个Iterable。

第四个是最令人震惊的 - 它立即使用所有可用的内存并引发OOM异常。即使更奇怪的是,它为我测试过的所有操作都做到了这一点:放置,贴图和过滤器。看看这些实现,它们中没有一个似乎保持很多状态(尽管这个下降看起来有点可疑 - 为什么它不只是计算这些项目?)。如果我不做任何操作,它工作正常。

我的猜测是,它保持对每行读取的引用,尽管我无法想象如何。当我在Scala中传入Iterables时,我看到了相同的内存使用情况。例如,如果我将情况3(.toIterable)并将其传递给一个将Iterable [String]写入文件的方法,我会看到同样的爆炸。

任何想法?

回答

6

注意如何ScalaDoc of Iterable说:

这种特质的

的实现需要提供一个具体的方法与 签名:

def iterator: Iterator[A] 

他们还需要提供一种方法newBuilder它创建了一个建设者 对于同一类型的集合。

既然你不提供newBuilder的实现,你会得到默认的实现,它采用了ListBuffer,因此试图以适应一切到内存中。

你可能想实现Iterable.drop作为

def drop(n: Int) = iterator.drop(n).toIterable 

但将与集合库表示不变性(即iterator.toIterable返回Stream打破,而你想List.drop返回一个List等 - 因此需要为Builder概念)。

+1

有趣...我来自C#所有的照顾。出于好奇 - 为什么他们会选择将整个序列作为默认选项缓冲? –

+0

这是否也意味着当我将一个序列作为Iterable [T]参数传递时,它将默认被缓冲?如果是这样,那不是失败的目的吗?我的印象是,当我通过toList,toArray等明确要求数据时,数据只会缓存在内存中。 –

+0

我并没有真正有资格评论集合库的设计(标准介绍主题是[here](http://www.artima.com/scalazine/articles/scala_collections_architecture.html))。你真的只会遇到问题,因为你试图扩展Iterable,你可以使用Stream或Iterator。 – themel