Java Scala不可变映射
我在创建地图时有一段代码,如:Java Scala不可变映射,java,performance,scala,functional-programming,bigdata,Java,Performance,Scala,Functional Programming,Bigdata,我在创建地图时有一段代码,如: 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]) 我正在阅读数百万行,并使用迭代器转换为MyObject。像 MyO
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])
我正在阅读数百万行,并使用迭代器转换为MyObject。像
MyObject("1", map)
当我这么做的时候,速度真的很慢,超过1小时,有2'000'000个条目
我从对象创建中删除贴图,但仍然执行拆分过程(第1节):
脚本在不到1分钟内运行的过程。对于200万条条目
我做了一些分析,看起来是在创建对象时,val映射
到对象映射之间的赋值使过程变慢了。我错过了什么
更新以更好地解释问题:
如果您看到我的代码来解释我对2000000行的自迭代,将每行转换为一个内部对象,那么要迭代,我会:
it.map(cretateNewObject).toList
此迭代器遍历所有行,并使用函数createNewObject
将它们转换为my对象
这实际上非常快,特别是使用dk14所说的大内存。性能问题在我的内部
`crateNewObject(val line:String)`
此函数用于创建对象
`class MyObject(val attribute1:String, val attribute2:Map[String, String])`
my函数首先执行这一行
`val attributeArr = line.split("\t")`
数组的第一个属性记录是my object的attribute1,第二个属性是
`val map = attributeArr(8).split(";").map(_ split "\"").collect { case Array(k, v) => (k, v) }.toMap`
如果我只打印map中的元素数量,程序将在2分钟内结束,如果我将map传递给我的新对象行MyObject(attribute1,map)
程序非常慢。(0到2000000)。toList
和(0到2000000)。map(x=>x->x)。如果给它们足够的内存,toMap
也有类似的性能(我尝试了-Xmx4G-4G字节)。toMap的实现很大程度上与克隆有关,因此大量内存被“分配”/“解除分配”。因此,在内存不足的情况下,GC变得过于活跃
当我试图用128Mb运行(0到2000000)。toList
(0到2000000)。这需要几秒钟的时间,但是(0到2000000)。map(x=>x->x)。toMap
在10%GC活动(VisualVM)的情况下至少需要2分钟,并因内存不足而死亡
然而,当我尝试-Xmx4G
时,两者都非常快
另外,toMap
所做的是重复地将一个元素添加到前缀树中,因此它必须对每个元素进行大量克隆(Array.copy
)
因此,toMap
重复(2000000次)执行updated0
,这反过来又执行一个数组。复制
非常频繁,这需要大量内存分配,这(在内存不足的情况下)会导致GC在大部分时间(从jconsole中可以看到)执行标记和扫描(缓慢的垃圾收集)
解决方案:无论是增加内存(-Xmx
/-Xms
JVM参数),还是需要对数据集执行更复杂的操作,都可以使用apachespark(或任何面向批处理的map-reduce框架)以分布式方式处理数据。在第二种情况下,您可能不会在任何地方使用val-map
,因此编译器只是将该行中的代码扔掉,而不会拆分任何内容。Hi@kolman不是这种情况,因为gtfLineArr(8).split(;”).map(split“\”).collect{case-Array(k,v)=>(k,v)}.toMap
计算分割并将结果转换为一个映射。实际上,我做了gtfLineArr(8).split(“;”).Map(\usplit“\”)。收集{case Array(k,v)=>(k,v)}.toMap.size
并打印结果,这一分钟意味着映射被创建。我怀疑的是,当映射被传递到新对象时,正在做“一些”造成问题的转换。您的代码还做了什么?这些行中可能没有花费时间。Dima不是真的。正如我所说。当我通过null删除映射到构造函数的赋值时。那么程序真的很快。只需尝试(0到2000000)。toList
vs(0到2000000)。map(x=>x->x).toMap
以获得纯结果我知道.toMap
非常耗时。但是,如果您看到示例,该操作仅对每个条目执行,而不是对列表进行计数。我一直在进行一些分析,似乎每次都收集为映射[String,String]
,并且映射被传递给对象(对于200万个条目中的每个条目)从char[]进行一些转换执行to String。正如我在示例中所说,我尝试运行的实际代码如下:@ypriverol不是。toMap
很耗时,是因为映射使用了大量内存。如果保留MyObject
的实例,那么第一个被剪切的实例需要比第二个实例更多的内存才能运行。
`val map = attributeArr(8).split(";").map(_ split "\"").collect { case Array(k, v) => (k, v) }.toMap`