图1 单Executor节点内存布局 我们可以看到在Yarn集群管理模式中,Spark 以 Executor Container 的情势在 NodeManager 中运行,其可使用的内存上限由 yarn.scheduler.maximum-allocation-mb 指定,我们称之为 MonitorMemory。
整个Executor内存区域分为两块:
1、JVM堆外内存
大小由 spark.yarn.executor.memoryOverhead 参数指定。默认大小为 executorMemory * 0.10, 最小384m。
此部门内存紧张用于JVM自身,字符串, NIO Buffer(Driect Buffer)等开销。此部门为用户代码及Spark 不可操作的内存,不足时可通过调整参数办理。
The amount of off-heap memory (in megabytes) to be allocated per executor. This is memory that accounts for things like VM overheads, interned strings, other native overheads, etc. This tends to grow with the executor size (typically 6-10%).
2、堆内内存(ExecutorMemory)
大小由 Spark 应用步伐启动时的 –executor-memory 或 spark.executor.memory 参数设置,即JVM最大分配的堆内存 (-Xmx)。Spark为了更高效的使用这部门内存,对这部门内存进行了逻辑上的划分管理。我们在下面的同一内存管剖析具体先容。
【注意】对于Yarn集群,存在: ExecutorMemory + MemoryOverhead <= MonitorMemory,若应用提交之时,指定的 ExecutorMemory 与 MemoryOverhead 之和大于 MonitorMemory,则会导致 Executor 申请失败;若运行过程中,现实使用内存超过上限阈值,Executor 进程会被 Yarn 克制掉 (kill)。
同一内存管理
图5 同一内存管理机制 其中最紧张的优化在于动态占用机制,其规则如下:
1、步伐提交的时间我们都会设定基本的 Execution 内存和 Storage 内存区域(通过 spark.memory.storageFraction 参数设置)。我们用 onHeapStorageRegionSize 来表现 spark.storage.storageFraction 划分的存储内存区域。这部门内存是不可以被驱逐(Evict)的存储内存(但是假如空闲是可以被占用的)。
2、当盘算内存不足时,可以借用 onHeapStorageRegionSize 中未使用部门,且 Storage 内存的空间被对方占用后,必要等候实行内存本身释放,不能抢占。
3、若现实 StorageMemory 使用量超过 onHeapStorageRegionSize,那么当盘算内存不足时,可以驱逐并借用 StorageMemory – onHeapStorageRegionSize 部门,而 onHeapStorageRegionSize 部门不可被抢占。
4、反之,当存储内存不足时(存储空间不足是指不足以放下一个完整的 Block),也可以借用盘算内存空间;但是 Execution 内存的空间被存储内存占用后,是可让对方将占用的部门转存到硬盘,然后“归还”借用的空间。
5、假如两边的空间都不足时,则存储到硬盘;将内存中的块存储到磁盘的策略是按照 LRU 规则进行的。 阐明
1、出于兼容旧版本的应用步伐的目的,Spark 仍旧保存了它的实现。可通过设置 spark.memory.useLegacyMode 参数启用。
2、spark.memory.storageFraction 是不可被驱逐的内存空间。只有空闲的时间能够被实行内存占用,但是不能被驱逐抢占。
Amount of storage memory immune to eviction, expressed as a fraction of the size of the region set aside by spark.memory.fraction. The higher this is, the less working memory may be available to execution and tasks may spill to disk more often. Leaving this at the default value is recommended. For more detail, see this description.
3、Storage 内存的空间被对方占用后,目前的实现是无法让对方”归还”,因为必要考虑 Shuffle 过程中的许多因素,实现起来较为复杂;而且 Shuffle 过程产生的文件在后面一定会被使用到,而 Cache 在内存的数据不一定在后面使用。在 Unified Memory Management in Spark 1.6 中具体讲解了为何选择这种策略,简朴总结如下:
3.1、数据扫除的开销 : 驱逐storage内存的开销取决于 storage level,MEMORY_ONLY 可能是最昂贵的,因为必要重新盘算,MEMORY_AND_DISK_SER 恰好相反,只涉及到磁盘IO。溢写 execution 内存到磁盘的开销并不昂贵,因为 execution 存储的数据格式紧凑(compact format),序列化开销低。而且,扫除的 storage 内存可能不会被用到,但是,可以预见的是,驱逐的 execution 内存是一定会再被读到内存的,频仍的驱除重读 execution 内存将导致昂贵的开销。
3.2、实现的复杂度 : storage 内存的驱逐是轻易实现的,只必要使用已有的方法,drop 掉 block。execution 则复杂的多,起首,execution 以 page 为单元管理这部门内存,而且确保相应的操作至少有 one page ,假如把这 one page 内存驱逐了,对应的操作就会处于饥饿状态。别的,还必要考虑 execution 内存被驱逐的环境下,等候 cache 的 block 如那里理。
4、上面说的借用对方的内存必要借用方和被借用方的内存类型都一样,都是堆内内存大概都是堆外内存,不存在堆内内存不够去借用堆外内存的空间。
使命内存管理(Task Memory Manager)