我在创建地图时有一段代码:
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)
当我这样做的时候非常慢,超过1小时的2&000; 000&000; 000条目。
我从对象创建中删除了地图,但我仍然执行拆分过程(第1部分):
val map = gtfLineArr(8).split(";").map(_ split "\"").collect { case Array(k, v) => (k, v) }.toMap
MyObject("1", null)
脚本运行的过程不到1分钟。对于2&000; 000和000,000,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`
如果我只打印地图中的元素数量,程序会在2分钟后结束,如果我将地图传递给新的对象行MyObject(attribute1, map)
,程序就会很慢。
答案 0 :(得分:3)
(0 to 2000000).toList
和(0 to 2000000).map(x => x -> x).toMap
具有相似的性能,如果你给他们足够的内存(我试过-Xmx4G - 4千兆字节)。 toMap
实现了很多关于克隆的内容,因此大量内存被“分配”/“解除分配”。因此,在内存不足的情况下,GC变得过度活跃。
当我尝试使用128Mb运行(0 to 2000000).toList
时 - 花了几秒钟,但(0 to 2000000).map(x => x -> x).toMap
至少花了2分钟进行10%GC活动(VisualVM),并因内存不足而死亡。
然而,当我尝试-Xmx4G
时,两者都非常快。
P.S。 toMap
所做的是重复向前缀树添加元素,因此每个元素都必须克隆(Array.copy
):https://github.com/scala/scala/blob/99a82be91cbb85239f70508f6695c6b21fd3558c/src/library/scala/collection/immutable/HashMap.scala#L321。
因此,toMap
重复(2000000次)执行updated0
,而Array.copy
反过来经常执行-Xmx
,这需要大量的内存分配(在低内存中) case)导致GC在大多数时间内进入MarkAndSweep(慢速垃圾收集)(据我所见,从jconsole)。
解决方案:是否增加内存(-Xms
/ temp1
JVM参数)或者如果您需要对数据集执行更复杂的操作,请使用Apache Spark(或任何面向批处理的map-reduce)框架)以分布式方式处理您的数据。