内存机制

在执行Spark的应用程序时,Spark集群会启动Driver和Executor两种JVM进程,前者为主控进程,后者负责执行具体的计算任务。

由于Driver的内存管理相对简单,本文主要对Executor的内存管理进行分析,下文中的Spark内存均特指Executor的内存。

1.堆内存和堆外内存

由于spark还是JVM进程,所以Executor 的内存管理建立在 JVM 的内存管理之上,因此存在着堆的概念,

堆内存受到 JVM 统一管理,它的GC是有一定算法逻辑的,当spark任务需要申请和释放内存的时候,并不是那么的自由灵活,JVM 对于内存的清理是无法准确指定时间点的,因此无法实现精确的释放。

堆外内存,使之可以直接在工作节点的系统内存中开辟空间,进一步优化了内存的使用,不受JVM GC的管控,由于序列化的数据占用的空间可以被精确计算,所以相比堆内内存来说降低了管理的难度,也降低了误差

1.1 堆内存

Executor 内运行的并发任务共享 JVM 堆内存,他们按照用途共分为3类。

Storage:缓存 RDD 数据和广播变量数据

Execution:执行Shuffle时占用的内存

剩余空间:不做特殊规划,存储Spark内部的对象实例,和用户定义的Spark应用程序中的对象实例

Spark虽然不能精准控制堆内内存的申请和释放,但通可调整storage+executor总共占内存的百分比,比如更改参数配置spark.memory.fraction

1.2 堆外内存

Spark引入了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间,存储经过序列化的二进制数据

默认情况下堆外内存并不启用,启用参数:spark.memory.offHeap.enabled

堆外内存大小参数:spark.memory.offHeap.size。

没有 other空间,堆外内存与堆内内存的划分方式相同,所有运行中的并发任务共享存储内存和执行内存

2. 内存空间分配

2.1 早期的静态内存管理

spark 1.6之前的内存管理较为简单,采用静态内存管理机制,用户可在启动前进行配置各个区域大小占比,但是运行过程中,各内存区间的大小均是固定的。

堆内内存默认 Storage:Execution:Other=6:2:2,由于 Spark 堆内内存大小的记录是不准确的,Storage 内存和 Execution 内存都有预留空间防止OOM

堆外内存没有other区,默认Storage:Execution=1:1。堆外内存不需要预留空间

2.2 统一内存管理机制

Spark1.6之后引入的统一内存管理机制,与静态内存管理的区别在于存储内存和执行内存共享同一块空间,可以动态占用对方的空闲区域。

堆内内存模型如图:

 

 堆外内存模型如图:

 

 

统一内存管理做了如下优化(注意只在Storage和Execution之间开了这个窗口,other还是固定的):

(1)双方的空间都不足时,则存储到硬盘;若己方空间不足而对方空余时,可借用对方的空间;(存储空间不足是指不足以放下一个完整的 Block)

(2)Execution执行内存的空间被对方占用后,可让对方将占用的部分转存到硬盘,然后"归还"借用的空间

(3)存储内存的空间被Execution执行内存占用后,无法让对方"归还",因为需要考虑 Shuffle 过程中的很多因素,实现起来较为复杂

 

 

3. 存储内存(Storage)缓存RDD怎么存储

对于存储内存来说,Spark 用一个 LinkedHashMap 来集中管理所有的 Block,Block 由需要缓存的 RDD 的 Partition 转化而成;

Storage模块在逻辑上以Block为基本存储单位,RDD的每个Partition经过处理后唯一对应一个Block(BlockId 的格式为 rdd_RDD-ID_PARTITION-ID )。

 

在具体实现时Driver端和Executor端的Storage模块构成了主从式的架构,即Driver端的BlockManager为Master,Executor端的BlockManager为Slave。Driver端的Master负责整个Spark应用程序的Block的元数据信息的管理和维护,而Executor端的Slave需要将Block的更新等状态上报到Master,同时接收Master的命令,例如新增或删除一个RDD。

4. 执行内存(Execution)怎么管理

Spark 用 AppendOnlyMap 来存储 Shuffle 过程中的数据,在 Tungsten 排序中甚至抽象成为页式内存管理,开辟了全新的 JVM 内存管理机制。

4.1 Shuffle Write

(1)若在 map 端选择普通的排序方式,会采用 ExternalSorter 进行外排,在内存中存储数据时主要占用堆内执行空间。

(2)若在 map 端选择 Tungsten 的排序方式,则采用 ShuffleExternalSorter 直接对以序列化形式存储的数据排序,在内存中存储数据时可以占用堆外或堆内执行空间,取决于用户是否开启了堆外内存以及堆外执行内存是否足够。

4.2 Shuffle Read

(1)在对 reduce 端的数据进行聚合时,要将数据交给 Aggregator 处理,在内存中存储数据时占用堆内执行空间。

(2)如果需要进行最终结果排序,则要将再次将数据交给 ExternalSorter 处理,占用堆内执行空间。

posted on 2020-12-31 17:15  happygril3  阅读(167)  评论(0)    收藏  举报

导航