1、前 言
Apache Spark是目前大数据领域主流的内存计算引擎,无论是在批处理还是实时流处理方面都有着广泛的应用。我们跑作业的时候,首先要给Spark Job分配一定的资源,比如一个executor分配5G内存,有时候我们会纠结于executor的内存有多少用于了实际计算。因此就需要了解一下Spark的内存管理,还有就是掌握了Spark的内存模型对于优化我们的作业也至关重要。
在Spark 1.5及之前版本中,内存管理默认实现是StaticMemoryManager,称为静态内存管理。从Spark 1.6.0版本开始,Spark默认采用一种新的内存管理模型UnifiedMemoryManager,称为统一内存管理,其特点是可以动态调整Execution和Storage的内存,参见SPARK-10000,因此又称为动态内存管理。本文我们就以Executor为例,介绍一下Spark的统一内存管理。
2、Spark内存管理
首先我们看一下Spark的内存使用,主要分为两类:Execution Memory和Storage Memory。其中,Execution Memory主要用于计算,如shuffles、joins、sorts及aggregations等操作,Storage Memory主要用于cache数据和在集群内部传输数据。
Executor默认只使用堆内内存(On-heap Memory)。为了进一步优化内存的使用,Spark引入了堆外内存(Off-heap Memory),默认是关闭状态。接下来,将详细说明Spark Executor堆内内存与堆外内存的具体情况。
2.1 On-heap Memory
Spark Executor通过spark.executor.memory或--executor-memory配置的内存为堆内内存,可以分为以下四块区域:
-
Execution Memory:主要用于shuffles、joins、sorts及aggregations等计算操作,又称为Shuffle Memory。
-
Storage Memory:主要用于cache数据、unroll数据,有时也被称为Cache Memory。
-
User Memory:用户内存,主要用于存储内部元数据、用户自定义的数据结构等,根据用户实际定义进行使用。
-
Reserved Memory:默认300M的系统预留内存,主要用于程序运行,参见SPARK-12081。
各个区域内存情况,如下图所示:
以上图解中,参数说明如下:
-
参数spark.memory.fraction在Spark 1.6版本中默认0.75,即Spark Memory(Execution Memory + Storage Memory)默认占整个usableMemory(systemMemory - Reserved Memory)内存的75%,而在Spark 2.x版本中默认0.6,默认占usableMemory内存的60%。
-
参数spark.memory.storageFraction默认0.5,代表Storage Memory占用Spark Memory百分比,(1 - spark.memory.storageFraction)代表Execution Memory占用Spark Memory百分比,默认值0.5表示Spark Memory中Execution Memory和Storage Memory各占一半。
2.2 Off-heap Memory
为了进一步优化内存的使用,减小GC开销,Spark 1.6版本还增加了对Off-heap Memory的支持,参见SPARK-11389,但Off-heap Memory默认是关闭的,开启须设置参数spark.memory.offHeap.enabled为true,并通过参数spark.memory.offHeap.size设置堆外内存大小,单位为字节。
堆外内存划分上没有了用户内存与预留内存,只包含Execution Memory和Storage Memory两块区域,内存情况如下图所示:
以上图解中,maxOffHeapMemory大小就是spark.memory.offHeap.size的值,spark.memory.storageFraction默认值不变。
3、动态内存分配
在如上两个图解中,我们可以看到Execution Memory与Storage Memory之间有一条可以上下移动的虚线,说明Execution Memory与Storage Memory不是固定不变的,彼此之间可以相互共享,这便是Spark动态内存管理的含义。Spark 1.5及之前版本,两者是固定不变的,即前文提及的静态内存管理。
意思是说,当Execution Memory有空闲,Storage Memory不足时,Storage Memory可以借用Execution Memory,反之亦然。Execution Memory可以让Storage Memory写到磁盘,收回被占用的空间。如果Storage Memory被Execution Memory借用,因为实现上的复杂度,却收回不了空间。
Spark 1.6版本开始,默认使用动态(统一)内存管理模型,但之前的静态内存管理模型(StaticMemoryManager)仍然保留,通过称为Legacy模式的参数spark.memory.useLegacyMode控制,默认false为不开启静态内存管理。
4、总 结
Apache Spark从1.6.0版本开始,其内存管理模块默认采用了动态内存管理模型,一直延续使用到Spark 2.x。本文参考了社区的一些分享,结合相关图解,从Spark总体内存使用、堆内内存、堆外内存等几个方面,重点对Spark的动态内存管理这块做了简单介绍。
往期推荐
1、HBase最佳实践 | 聊聊HBase核心配置参数
2、Apache Hudi:剑指数据湖的增量处理框架
3、Hadoop社区比 Ozone 更重要的事情
4、MapReduce Shuffle 和 Spark Shuffle 结业篇