Spark内存管理模型详解

本文首发于 www.yidooo.net/2018/07/29/… 转载请注明出处

引言

Spark 作为一个基于内存的分布式计算引擎,其内存管理模块在整个系统中扮演着非常重要的角色。理解 Spark 内存管理的基本原理,有助于更好地开发 Spark 应用程序和进行性能调优。

一个Spark Application一般包括Driver和Executor两种JVM进程。Driver为主控进程,负责创建Context,提交Job,并将Job转换为Task,协调Executor间的Task执行。而Executor主要负责执行具体的计算任务,将结果返回Driver。 由于Driver的内存管理比较简单,和一般的JVM程序区别不大,所以本文重点分析Executor的内存管理。所以,本文提到的内存管理都是指Executor的内存管理。

堆内内存和堆外内存

Executor作为一个JVM进程,它的内存管理是基于JVM之上的。所以JVM的内存管理包括两种方式:

  • 堆内内存管理(On-Heap):对象分配的在JVM的堆上,对象会受GC束缚。
  • 堆外内存管理(Off-Heap):对象通过序列化分配在JVM之外的内存里,由应用程序对其进行管理,且不受GC束缚。这种内存管理方式可以避免频繁的 GC,但缺点是必须自己编写内存申请和释放的逻辑。

一般来说对象读写速度是:on-heap > off-heap > disk

内存空间分配

在Spark中,支持两种内存管理方式:静态内存管理(Static Memory Manager)和统一内存管理(Unified Memory Manager)。

Spark为Storage内存和Execution内存的管理提供了统一的接口MemoryManager,同一个 Executor内的任务都调用这个接口的方法来申请或释放内存。MemoryManager的实现上,Spark 1.6以前默认采用的是静态内存管理([StaticMemoryManager]((github.com/apache/spar…))的方式;而在Spark1.6以后,默认采用的是统一内存管理(UnifiedMemoryManager)的方式。在中Spark 1.6+中,可以通过spark.memory.useLegacyMode参数启用静态内存管理。

静态内存管理(Static Memory Manager)

静态内存管理机制下,Storage内存、Execution内存和其他内存的大小在 Spark 应用程序运行期间均为固定的,但用户可以应用程序启动前进行配置。由于这种分配已经逐渐被淘汰,但出于兼容性考虑,Spark依然保留下来。有兴趣的话,可以参考:blog.csdn.net/Lin_wj1995/…

这边主要讲下静态内存管理的弊端:静态内存管理机制实现起来较为简单,但如果用户不熟悉Spark的存储机制,或没有根据具体的数据规模和计算任务或做相应的配置,很容易造成Storage内存和Execution内存中的一方剩余大量的空间,而另一方却早早被占满,不得不淘汰或移出旧的内容以存储新的内容。

统一内存管理(Unified Memory Manager)

Spark 1.6之后引入了统一内存管理机制,该机制与静态内存管理的区别在于,Storage内存和Execution内存是共享一块内存空间的,双方可以互相占用对方的空闲区域。

堆内模型

默认情况下,Spark仅使用了堆内内存。堆内内存的大小由Spark Application启动时的--executor-memory或spark.executor.memory 参数配置。Executor内运行的并发任务共享JVM堆内内存。

Executor端的堆内内存区域大致可以分为以下四大块:

  • Storage内存(Storage Memory):主要用于存储Spark的cache数据,例如RDD的缓存、Broadcast变量,Unroll数据等。
  • Execution内存(Execution Memory):主要用于存放 Shuffle、Join、Sort、Aggregation等计算过程中的临时数据。
  • 用户内存(User Memory):主要用于存储 RDD 转换操作所需要的数据,例如 RDD 依赖等信息。
  • 预留内存(Reserved Memory):系统预留内存,会用来存储Spark内部对象。

内存分布如下图所示:

堆外模型

Spark 1.6 开始引入了Off-heap memory(SPARK-11389)。默认情况下,堆外内存是关闭的,我们可以通过spark.memory.offHeap.enabled参数启用,通过spark.memory.offHeap.size设置堆外内存大小。相比堆内内存,堆外内存的模型比较简单,只包括Storage内存和Execution内存,其分布如下图所示:

如果堆外内存被启用,那么Executor内将同时存在堆内和堆外内存,两者的使用互补影响,这个时候Executor中的Execution内存是堆内的Execution 内存和堆外的Execution内存之和,同理,Storage内存也一样。下图为Spark堆内和堆外示意图

动态占用机制

  • 在程序提交时,会根据spark.memory.storageFraction参数设置Storage内存区域和Execution内存区域。
  • 在程序运行时,如果双方的空间不不足(存储空间不足以放下一个完整的Block),则按照LRU规则存储到磁盘;如果己方空间不足而对方空间有空余,则借用对方的空间。
  • Storage占用对方内存,可将占用的部分转存到硬盘,然后"归还"借用的空间。
  • Execution占用对方内存,目前的实现是无法让对方"归还"的。因为Shuffle过程产生的文件在后面一定会被使用到,而Cache在内存的数据不一定在后面使用,归还内存可能会导致性能严重下降。

参考资料

转载于:https://juejin.im/post/5b94937e6fb9a05cd456c337

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值