Spark运行是内存分为三部分,执行内存(execute memory),存储内存(storge memory),预留内存(reserved memory).在1.6版本以前执行内存和存储内存是静态分配的,意思着应用一启动后,各区域的内存大小就是不变的。这就会带来一个后果,有时执行内存严重不足,但是存储内存又都没怎么用,或者相反。因为这个问题,spakr1.6版本之后引入的动态内存管理机制。本文就是主要讲讲这一部分的内存。
一、新旧内存管理
1、旧有(1.6版本之前)的内存管理
概念上,内存空间被分成了三块独立的区域,每块区域的内存容量是按照JVM堆大小的固定比例进行分配的:
Execution:在执行shuffle、join、sort和aggregation时,用于缓存中间数据。通过spark.shuffle.memoryFraction进行配置,默认为0.2。
Storage:主要用于缓存数据块以提高性能,同时也用于连续不断地广播或发送大的任务结果。通过`spark.storage.memoryFraction进行配置,默认为0.6。
Other:这部分内存用于存储运行Spark系统本身需要加载的代码与元数据,默认为0.2。
无论是哪个区域的内存,只要内存的使用量达到了上限,则内存中存储的数据就会被放入到硬盘中,从而清理出足够的内存空间。这样一来,由于与执行或存储相关的数据在内存中不存在,就会影响到整个系统的性能,导致I/O增长,或者重复计算。
2、1.6版本之后
到了1.6版本,Execution Memory和Storage Memory之间支持跨界使用。当执行内存不够时,可以借用存储内存,反之亦然。
1.6版本的实现方案支持借来的存储内存随时都可以释放,但借来的执行内存却不能如此。
新的版本引入了新的配置项:
spark.memory.fraction(默认值为0.75):用于设置存储内存和执行内存占用堆内存的比例。若值越低,则发生spill和evict的频率就越高。注意,设置比例时要考虑Spark自身需要的内存量。
spark.memory.storageFraction(默认值为0.5):显然,这是存储内存所占spark.memory.fraction设置比例内存的大小。当整体的存储容量超过该比例对应的容量时,缓存的数据会被evict。
spark.memory.useLegacyMode(默认值为false):若设置为true