JVM调优
问题概述:
RDD缓存、task定义的运行的算子函数,可能会创建很多对象,这样会占用大量的堆内存,
堆内存满了以后会频繁的GC,如果GC依然还不能满足的内存的需要的话就会报OOM错误。
比如一个task在运行的时候会创建N多个对象,这些对象首先会放到JVM年轻代里,
比如再存数据的时候我们使用了foreach来将数据写入到内存,那么又多少数据就会创建多少个对象(将数据封装到自定义对象里存到数据库),又一千万条数据就会创建一千万个对象。
JVM结构
年轻代,老年代,永久代(方法区)
对象生命周期
在task中创建的对象首先会往 伊甸园(eden)和 survior1 中存放,survior2是空闲的,
当 eden 和 survior1区域放满之后 就会触发 minor gc (小型垃圾回收 ),清理掉不再使用的对象(死亡),再将存活下来的对象放入survior2中
如果存活下来的对象大于survior2的大小,那么JVM就会通过担保机制将 多余的对象直接放入到老年代中。
如果存活下来的对象大于survior2的大小,那么JVM就会通过担保机制将 多余的对象直接放入到老年代中。
如果这个时候 年轻代的内存不是很大 的话就会 经常的进行 minor gc
频繁的minor gc 会导致一段事件内会导致短生命周期的对象(不一定长期使用,没进行过一次垃圾回收 )频繁的倒来倒去(多次垃圾回收 都没有回收掉,一直在用的又不能备释放)。进行一次minor gc就会增长一岁,
年龄超过15岁,垃圾回收还是没有回收的对象就会跑到老年代里面了。
这样会导致 老年代 中存在大量的短生命周期对象,老年代应该存放的数量比较少且长期使用的对象,比如数据库连接池。
这样老年代就会满溢(full gc),因为本来老年代的对象很少,很少进行full gc,因此采取了不太复杂但是消耗性能和时间的垃圾回收算法。
不管minor gc还是full gc都会导致 JVM工作线程停止
总结概括:堆内存不足造成的影响
1、很多短周期对象造成频繁的minor gc.
2、老年代中有大量的短周期对象会导致full gc.
3、有了gc就会影响Spark的性能和运行速度.
处理方法:
Spark堆内存使用情况 :
RDD缓存数据占0.6 , 对象(task运行产生)占0.2 , shuffle聚合内存占 0.2
将RDD缓存数据的内存变小,那么task运行内存就变大了
spark.storage.memoryFraction 默认是0.6 ,减小此值
如何查找问题呢?
访问Driver端口4040,一步步点进去,找到task: