与Hadoop、HBase生态圈的众多项目等一样,Spark的运行离不开JVM的支持,同时由于Spark立足于内存计算,常常需要在内存中存放大量数据,再加上Spark同时支持批处理和流式处理,对于程序吞吐量和延迟都有较高要求,所以Spark对于JVM垃圾回收机制(GC)的依赖更加突出。而垃圾回收是JVM的“死穴”,它会影响程序的性能甚至还会带来系统卡顿,所以GC参数的调优在Spark应用实践中显得尤为重要。
首先需要获取一些统计信息,包括内存回收的频率、内存回收耗费的时间等。为了获取这些统计信息,可以把参数-verbose:gc-XX:+PrintGCDetails-XX:+PrintGCTimeStamps添加到环境变量SPARK_JAVA_OPTS中。设置完成后,Spark作业运行时,可以在日志中看到每一次垃圾回收的详细情况。需要注意的是,这些信息保存在集群的Executors中而不是Driver中。
Spark垃圾回收调优的目标是确保只有长时间存活的RDD才保存到老生代区域;同时,新生代区域需要足够大以保存生命周期比较短的对象。这样,在任务执行期间可以避免执行full GC。下面是一些可能有用的执行步骤。(www.xing528.com)
通过收集GC信息检查内存回收是否过于频繁,如果在任务结束之前执行了很多次full GC,则表明任务执行的内存空间不足;在打印的内存回收信息中,如果老生代接近消耗殆尽,那么减少用于缓存的内存空间,这可以通过配置属性spark.storage.memoryFraction来完成,通过减少缓存对象来提高执行速度是非常值得的;如果有过多的minor GC而不是full GC,那么为Eden分配更大的内存是有益的,可以为Eden分配大于任务执行所需要的内存空间。如果Eden的大小确定为E,那么可以通过-Xmn=4/3×E来设置新生代的大小(将内存扩大到4/3是考虑到survivor所需要的空间)。举一个例子,如果任务从HDFS读取数据,那么任务需要的内存空间可以从读取的block数量估算出来。注意解压后的blcok通常为解压前的2~3倍。所以,如果需要同时执行3或4个任务,block的大小为64 M,可以估算出Eden的大小为4×3×64 MB;监控内存回收的频率及消耗的时间,并修改相应的参数设置。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。