首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Scala Source.fromFile内存消耗

Scala Source.fromFile内存消耗
EN

Stack Overflow用户
提问于 2016-04-19 04:06:58
回答 2查看 532关注 0票数 0

全,

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

下面的代码导致堆从正常大小的100MB膨胀到1.7GB,尽管要加载的底层数据只有120MB

我可以在这里做得更好吗?

代码语言:javascript
复制
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)

EN

回答 2

Stack Overflow用户

发布于 2016-04-20 03:39:54

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

我们要做的是:

  • 在该迭代器上以Iterator[(String, String)].
  • fold的形式打开文件,同时构建一个Map[String, List[String]] -其中的关键字是帐户if和值基准数据。
  • 如果您真的对Foo case类感兴趣,请将映射转换为该类的列表。

不用再多说了:

代码语言:javascript
复制
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,或者dates -那就用它们吧。int比它的字符串表示使用更少的内存。

让我知道结果如何!

票数 3
EN

Stack Overflow用户

发布于 2016-04-19 15:53:01

在将典型文件读入内存时,文件大小会自动加倍,因为这会将单字节字符表示转换为双字节JVM字符。然后,由于Oracle在Java7的一个点版本中所做的更改,当您将输入拆分成子字符串时,您将增加一倍以上(在此之前,更改的子字符串引用了原始字符串的后备数组,但这在- not -广泛使用的Glassfish中导致了一个问题,因此Oracle更改了JVM的行为,将子字符串字符复制到一个新的数组中;因为您仍然拥有对原始字符串以及子字符串的引用,所以内存使用量增加了一倍以上)。

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

所以我猜这会将你的使用率降低到1.7 to的一半。其余的可能是由于在raw.groupBy语句期间创建的临时结构造成的,尽管我预计大多数临时结构将在之后发布。

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36703349

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档