赞
踩
众所周知,Spark是比Hadoop快近百倍(理想条件下,如下图所示)的大数据计算引擎,而这其中最主要突出的地方就是Spark是基于内存的计算引擎,相比于Hadoop来说,减少了MR过程中的磁盘IO,通过将map端计算的中间结果存储到内存,reduce端在拉取中间结果的时候避免了大量的磁盘IO,从而提升了性能。因此,作为任务的所有计算都在内存中进行的引擎来说,深入了解Spark内存管理机制是非常重要的。
在进行分析Spark内存管理前,我们先看下Spark的架构图:
从上图可以看出,Spark Application中包括两个JVM进程:Driver和Executor
Spark的内存管理在这两个部分中也是有所不同的,其中Driver内存管理比较简单,这里不做讲解,我们主要分析的是Executor的内存管理。
Executor内存分为两种(如下图所示):In-Heap memory 和 External memory
下面先对这两种内存进行详细的介绍。
On-Heap 内存即为堆内内存,它的大小由提交作业时指定 --excutor-memory 参数配置。Spark对内存的管理是一种逻辑上的规划管理,具体内存的申请和释放由JVM完成,Spark只是在JVM内存申请后和释放前记录这些内存(这是因为Spark的内存管理是基于JVM内存管理的),对于序列化对象可以精确计算实际占用内存,对于非序列化对象,占用内存采用周期采样估算得出,这样就导致实际可用内存小于spark记录的可用内存,容易导致OOM异常,但通过划分不同区域进行管理,可一定程度上减少了异常的出现。下图为Spark堆内内存的分配图(图例为统一内存管理):
从上图我们可以看出,Spark堆内内存分为四个部分:
为了进一步优化内存的使用以及提高 Shuffle 时排序的效率,Spark 引入了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间,存储经过序列化的二进制数据。利用 JDK Unsafe API(从 Spark 2.0 开始,在管理堆外的存储内存时不再基于 Tachyon,而是与堆外的执行内存一样,基于 JDK Unsafe API 实现),Spark 可以直接操作系统堆外内存,减少了不必要的内存开销,以及频繁的 GC 扫描和回收,提升了处理性能。堆外内存可以被精确地申请和释放,而且序列化的数据占用的空间可以被精确计算,所以相比堆内内存来说降低了管理的难度,也降低了误差。
下图为Spark堆外内存的分配图(图例为统一内存管理):
在默认情况下堆外内存并不启用,可通过配置spark.memory.offHeap.enabled参数启用,并由spark.memory.offHeap.size参数设定堆外空间的大小。除了没有 other 空间,堆外内存与堆内内存的划分方式基本相同,所有运行中的并发任务共享存储内存和执行内存。
了解了Spark的内存分类后,我们就来看下Spark内存的管理机制。Spark的内存管理机制分为两种(如下图所示):静态内存管理和统一内存管理
下面对这两种内存管理机制进行详细的介绍。
Spark的静态内存管理将内存静态地分成两个固定的分区,Storage Memory和Execution Memory等内存的大小在应用程序处理过程中是固定的,但用户可以在应用程序启动前对其进行配置。设置参数如下可参考:
静态内存管理图示-堆内
静态内存管理图示-堆外
作为传统的内存管理模型,它的优缺点还是很明显的:
优点:
缺点:
注:从Spark 1.6.0开始,采用新的内存管理器替代静态内存管理器,静态管理方式任然被保留,可通过spark.memory.useLegacyMode参数启用。但静态内存分配方法在 Spark 3.0 中被淘汰了,不能使用了
Spark从1.6.0版本开始,采用新的内存管理器代替了静态内存管理器,为Spark提供动态内存分配。它分配一个内存区域作为存储和执行共享的统一内存容器。当不使用执行内存时,存储内存可以获得所有可用内存,反之亦然。如果任何存储或执行内存需要更多空间,一个名为acquireMemory() 的函数将扩展其中一个内存池并缩小另一个内存池。借用的存储内存可以在任何给定时间被逐出。然而,由于实现的复杂性,借用的执行内存不会在第一个设计中被逐出。
动态内存管理机制的动态占用规则如下:
动态内存管理图示-堆内
动态内存管理图示-堆外
动态内存管理机制是用来解决静态内存管理机制不够灵活的问题的,它的优点如下:
Spark内存管理相关类都在 spark core 模块的 org.apache.spark.memory 包下。
在Spark3.0之前,Spark有两种内存管理模式,静态内存管理(Static MemoryManager)和动态(统一)内存管理(Unified MemoryManager)
这个包实现了Spark的内存管理系统。该系统由两个主要组件的组成,即JVM范围内的内存管理和单个任务的内存管理:
在内部,这些组件中的每一个都有额外的内存记账抽象:
示意图:
MemoryManager有两种实现,它们处理内存池大小的方式各不相同:
在Spark3.0之后,静态内存管理被淘汰了,因此只剩下动态(统一)内存管理(Unified MemoryManager),因此内存管理源码有了较大改动。
本部分仅仅介绍Memory包中各模块的功能,详细内容见后续介绍。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。