创建地图时,我有一段代码:
val map = gtfLineArr(8).split(";").map(_ split "\"").collect { case Array(k, v) => (k, v) }.toMap
然后,我使用此映射创建我的对象:
case class MyObject(val attribute1: String, val attribute2: Map[String:String])
我正在读取数百万行,并使用迭代器转换为MyObjects。喜欢
MyObject("1", map)
当我这样做时,它真的很慢,需要2小时才能完成2 000 000个条目。
我从对象创建中删除了地图,但仍然进行拆分过程(第1节):
val map = gtfLineArr(8).split(";").map(_ split "\"").collect { case Array(k, v) => (k, v) }.toMap
MyObject("1", null)
脚本运行的过程不到1分钟。 2,000万个条目。
我进行了一些分析,看起来像是在创建对象时,
val map
与对象映射之间的分配使过程变慢。我缺少什么?更新以更好地解释问题:
如果您看到我的代码来解释我的自我迭代,那么我将遍历2000000行并将每行转换为内部对象,以进行迭代:
it.map(cretateNewObject).toList
此迭代器遍历所有行,并使用
createNewObject
函数将其转换为我的对象。这实际上非常快,特别是使用dk14所说的大内存。性能问题在我的内心
`crateNewObject(val line:String)`
这个函数创建一个对象
`class MyObject(val attribute1:String, val attribute2:Map[String, String])`
我的功能先行然后做
`val attributeArr = line.split("\t")`
数组的第一个属性记录是我的对象的attribute1,第二个属性是
`val map = attributeArr(8).split(";").map(_ split "\"").collect { case Array(k, v) => (k, v) }.toMap`
如果仅打印map中的元素数量,则程序将在2分钟内结束,如果将map传递给新的对象行
MyObject(attribute1, map)
,则该程序的运行速度将非常慢。 最佳答案
如果为它们提供足够的内存,(0 to 2000000).toList
和(0 to 2000000).map(x => x -> x).toMap
的性能类似(我尝试-Xmx4G-4 GB)。 toMap
实现与克隆有关,因此大量内存正在“分配” /“取消分配”。因此,如果出现内存不足的情况,GC将变得 Activity 过度。
当我尝试以128Mb运行(0 to 2000000).toList
时-花费了几秒钟,但是(0 to 2000000).map(x => x -> x).toMap
在10%GC Activity (VisualVM)下花费了至少2分钟,并死于内存不足。
但是,当我尝试-Xmx4G
时,它们都非常快。
附言toMap
所做的是在前缀树中反复添加一个元素,因此每个元素必须对其进行很多克隆(Array.copy
):https://github.com/scala/scala/blob/99a82be91cbb85239f70508f6695c6b21fd3558c/src/library/scala/collection/immutable/HashMap.scala#L321。
因此,toMap
重复执行(2000000次)updated0
,而这又经常执行Array.copy
,这需要大量的内存分配,这(在低内存情况下)导致GC大部分执行MarkAndSweep(缓慢的垃圾回收)。时间(据我从jconsole所看到的)。
解决方案:是增加内存(-Xmx
/ -Xms
JVM参数),还是需要对数据集进行更复杂的操作,请使用Apache Spark(或任何面向批处理的map-reduce框架)之类的方式以分布式方式处理数据。