Scala Source.from文件内存消耗



全部,

我有一个CSV文件,只有大约120MB(称之为demo.csv

以下代码导致堆从100MB的正常大小膨胀为1.7GB,尽管加载的底层数据仅为120MB

我在这里能做得更好吗?

case class Foo(x:String, y: Array[String])
....
val src = Source.fromFile(file)
val lines = src.getLines()
val raw = lines.map(_.split(",")).toArray
src.close()
/**
  * a map from accountId to their benchmark components
  */
val result = raw.groupBy(_.(0)).map {
  case (x, y) => Foo(x,y)
}.toArray

我知道toArray可能是这里的问题,但我确实需要groupBy。。。除非我把所有的东西都记在记忆里,否则我无法做到这一点。什么是替代方案?

我知道在groupBytoArray阶段,堆可能会暂时膨胀。但是,由于底层数据只有120MB,我的堆怎么可能永久上升>1G?(换句话说,无论保留什么,似乎都不是GC ed)

首先,我建议使用专用的CSV解析库-手动解析CSV比看起来复杂得多,有很多边缘情况(比如说,如果你的值中有一个包含换行符怎么办?)。我们将使用kantan.csv,因为我是作者,但有很多高质量的库。

我们要做的是:

  • Iterator[(String, String)]打开文件
  • 在迭代器上折叠,构建一个Map[String, List[String]],其中键是帐户ID和值基准数据
  • 如果您真的很喜欢Foo案例类,请将映射转换为该类的列表

事不宜迟:

import kantan.csv._     // kantan.csv core types.
import kantan.csv.ops._ // syntax.
case class Foo(id: String, data: List[String])
// Open the CSV file for reading, assuming ; as column separator
// and no header row.
input.asUnsafeCsvReader[(String, String)](';', false)
// Fold on the file, aggregating data in a map
  .foldLeft(Map.empty[String, List[String]]) { case (acc, (key, value)) =>
    acc + (key -> (value :: acc.getOrElse(key, List.empty)))
// Now that we have the whole data as a Map, turn that into a List[Foo].
  }.map(r => Foo(r._1, r._2))

这永远不会多次加载输入数据,一旦将每一行放入聚合映射中,就会丢弃它——而您的实现,如果我算对了,到最后在内存中有4次(一次作为行,一次作为分割行,一次作为List[Foo],一次为Array[Foo])。

此外,当你没有选择的时候,字符串是很好的,但如果你有更好的类型,比如int或date,那就用这些。int使用的内存比它的字符串表示要少得多。

让我知道结果如何!

当您将一个典型的文件读取到内存中时,大小会自动翻倍,因为这会将单字节字符表示转换为两字节JVM字符。然后,由于Oracle在Java 7的点发行版中所做的更改,当您将输入拆分为子字符串时,您将再次加倍(在上述更改之前,子字符串引用了原始字符串的后备数组,但这在广泛使用的-而不是的Glassfish中造成了问题,因此Oracle更改了JVM的行为,将子字符串字符复制到一个新数组中;因为您仍然可以引用原始字符串以及子字符串,因此您的内存使用量增加了一倍多)。

根据拆分字符串的长度,内存使用量可能会增加一倍以上——由于String对象本身和用于字符的Array对象,每个String占用的内存大约比字符表示中的实际字节数多40个字节。

所以我想这会让你的1.7GB使用量减少一半。其余的可能是由于raw.groupBy语句期间创建的临时结构,尽管我预计其中大部分将在之后发布。

在检查内存使用情况之前,您是否进行了一些延迟?为了进行垃圾收集,通常需要这样做。垃圾收集完成后,您应该能够合理估计运行时的实际内存使用情况。totalMemory()-runtime.freeMemory().

最新更新