做者编辑:杜晓蝶,王玮,任泽
Spark 静态内存管理详解
1、 内容简介
spark从1.6开始引入了动态内存管理模式,即执行内存和存储内存之间能够互相抢占。spark提供两种内存分配模式,即:静态内存管理和动态内存管理。该系列文章分别对这两种内存管理模式的优缺点以及设计原理进行了分析。该篇文章主要针对spark1.6静态内存管理进行了分析与说明。动态内存管理以及其余的调优文章后期会陆续为你们呈现,请你们关注furion。
此外本文会涉及到不少spark的概念,若是读者对spark比较陌生,能够在阅读本文前先了解一下基本概念,参考地址:http://www.jianshu.com/p/e41b18a7e202
2、 内存空间分配
在 Spark 最初采用的静态内存管理机制下,存储内存、执行内存和其余内存的大小在 Spark 应用程序运行期间均为固定的,但用户能够应用程序启动前进行配置,堆内内存的分配以下图所示:html
默认状况下,spark内存管理采用unified模式,若是要开启静态内存管理模式。将Spark.memory.useLegacyMode参数调为true(默认为false)。官网相关配置以下:算法
当调整该参数之后,从SparkEnv.scala中可知,若是为true,内存管理调用静态内存类(StaticMemoryManager)。反之,内存管理采用统一内存管理类(UnifiedMemoryManager)。apache
3、 Execution内存缓存
Execution内存在运行时会被分配给运行在JVM上的task。这里不一样的是,分配给每一个task的内存并非固定的,而是动态的。spark不是一上来就分配固定大小的内存块给task,而是容许一个task占据JVM全部execution内存。
每一个JVM上的task能够最多申请至多1/N的execution内存(N为active task的个数,由spark.executor.cores指定)。若是task的申请没有被批准,它会释放一部份内存,而且下次申请的时候,它会申请更小的一部份内存。
注意:为了防止过多的spilling(evict)数据,只有当一个task分配到的内存达到execution内存1/(2N)的时候才会spill, 若是目前空闲的内存达不到1/(2N)的时候, 内存申请会被阻塞直到其余的task spill掉它们的内存。若是不这样限制,假设当前有一个任务占据了绝大部份内存,那么新来的task会一直往硬盘spill数据,这样就会致使比较严重的I/O问题。
举个例子, 某executor先启动一个task A,并在task B启动前快速占用了全部可用内存。(B启动后)N变成2,task B会阻塞直到task A spill,本身可得到1/(2N)=1/4的execution内存。而一旦task B获取到了1/4的内存,A和B就都有可能spill了。数据结构
unroll的优先级仍是比较高的,它使用的内存空间能够从storage中借用,若是在storage中没有现存的数据block,它甚至能够占据整个storage空间。若是storage中有数据block,它能够最大drop掉内存的数据是以spark.storage.unrollFraction来控制的。由图6可知,这部分默认为storage的20%。
注意:这个20%的空间并非静态保留的,而是经过drop掉内存中的数据block来分配的。若是unroll失败了,spark会把这部分数据evict 到硬盘。
4、 Other部分
这片内存用于程序自己运行所需的内存,以及用户定义的数据结构和建立的对象,此内存有上面两部分决定,默认为0.2。
5、 局限性
spark的设计文档中指出静态内存有如下局限性:
(1) 没有适用于全部应用的默认配置,一般须要开发人员针对不一样的应用进行不一样的参数配置。好比根据任务的执行逻辑,调整shuffle和storage内存占比来适应任务的需求。
(2) 这样须要开发人员具有较高的spark原理知识。
(3) 那些不cache数据的应用在运行时只占用一小部分可用内存,由于默认的内存配置中,storage用去了safety内存的60%。
6、概念补充
eviction策略:在spark技术文档中,eviction一词常常出现。eviction并非单纯字面上驱逐的意思。说句题外话,spark咱们一般都把它叫作内存计算框架,严格意义来讲,spark并非内存计算的新技术。不管是cache仍是persist这类算子,spark在内存安排上,绝大多数用的都是LRU策略(LRU能够说是一种算法,也能够算是一种原则,用来判断如何从Cache中清除对象,而LRU就是“近期最少使用”原则,当Cache溢出时,最近最少使用的对象将被从Cache中清除)。即当内存不够的时候,会evict掉最远使用过的内存数据block。当evict的时候,spark会将该数据块evict到硬盘,而不是单纯的抛弃掉。
不管是storage仍是execution的内存空间,当内存区域的空间不够用的时候,spark都会evict数据到硬盘。
所以,若是开发人员在内存分配上没有合理的进行分配,不管是在storage仍是execution超过内存的限制的时候,spark会把内存的数据写到硬盘。若是是storage的状况,甚至可能把内存的数据所有写到硬盘并释放掉内存中的缓存数据。这样作,无疑会增长系统调用、I/O以及重复计算的开销。有过开发spark任务中包含大量shuffle stage的同窗应该有同感,shuffle memory不够的时候,spill到硬盘的数据会很大,致使任务很慢,甚至会致使任务的各类重试最后任务fail掉。这种状况建议提升shuffle memory fraction。若是是资源调度在yarn上,建议经过spark.yarn.executor.memoryOverhead提升堆外内存,有的时候甚至会调到2g,3g,4g直到任务成功。
7、参考
【1】 Unified Memory Management in Spark 1.6 ,Andrew Or and Josh Rosen
【2】https://www.ibm.com/developerw ... erral
【3】 http://spark.apache.org
【4】 http://www.jianshu.com/p/e41b18a7e202app