深入了解Spark内存管理模型

本文概述

  • 1.简介
  • 2.堆内存和堆外内存
  • 3.内存分配
1.简介 作为基于内存的分布式计算引擎, Spark的内存管理模块在整个系统中扮演着非常重要的角色。了解Spark内存管理的基础知识可帮助你开发Spark应用程序并执行性能调整。
通常, Spark应用程序包含两个JVM进程, 即驱动程序和执行程序。驱动程序是主要的控制过程, 它负责创建上下文, 提交作业, 将作业转换为任务以及协调执行程序之间的任务执行。执行程序主要负责执行特定的计算任务, 并将结果返回给驱动程序。因为Driver的内存管理相对简单, 并且一般的JVM程序之间的差异不大, 所以在本文中我将集中讨论Executor的内存管理。因此, 本文中提到的内存管理是指执行程序的内存管理。
2.堆内存和堆外内存 执行程序充当JVM进程, 其内存管理基于JVM。因此, JVM内存管理包括两种方法:
  • 堆上内存管理:对象在JVM堆上分配, 并由GC绑定。
  • 堆外内存管理:对象通过序列化在JVM之外的内存中分配, 由应用程序管理, 并且不受GC绑定。这种内存管理方法可以避免频繁的GC, 但是缺点是你必须编写内存分配和内存释放的逻辑。
通常, 对象的读写速度为:
堆上> 堆外> 磁盘
3.内存分配 在Spark中, 支持两种内存管理模式:静态内存管理器和统一内存管理器。
Spark提供了一个统一的接口MemoryManager来管理存储内存和执行内存。同一执行器中的任务调用该接口以申请或释放内存。在实现MemoryManager时, 默认情况下会在Spark 1.6之前使用StaticMemory管理, 而默认方法已在Spark 1.6之后更改为UnifiedMemoryManager。在Spark 1.6+中, 可以通过spark.memory.useLegacyMode参数启用静态内存管理。
3.1。静态内存管理器
【深入了解Spark内存管理模型】在静态内存管理器机制下, 存储内存, 执行内存和其他内存的大小在Spark应用程序运行期间是固定的, 但是用户可以在应用程序启动之前对其进行配置。尽管已逐渐取消了这种分配方法, 但出于兼容性原因, Spark仍然保留。
这里主要讨论静态内存管理器的弊端:静态内存管理器机制实现起来比较简单, 但是如果用户不熟悉Spark的存储机制, 或者不根据特定的数据大小进行相应的配置在计算和计算任务方面, 很容易导致其中一个存储内存和执行内存剩余大量空间, 而另一个内存首先被填满, 因此必须删除或删除旧内容以用于新内容。
3.2。统一内存管理器
Spark 1.6之后引入了统一内存管理器机制。统一内存管理器和静态内存管理器之间的区别在于, 在统一内存管理器机制下, 存储内存和执行内存共享一个内存区域, 并且两者都可以占据彼此的空闲区域。
3.2.1。堆上模型 默认情况下, Spark仅使用堆上内存。当Spark Application启动时, 堆内存由–executor-memory或spark.executor.memory参数配置。在Executor中运行的并发任务共享JVM的堆上内存。
执行程序中的堆上内存区域可以大致分为以下四个块:
  • 存储内存:主要用于存储Spark缓存数据, 如RDD缓存, Broadcast变量, Unroll数据等。
  • 执行内存:主要用于在Shuffle, Join, Sort, Aggregation等计算过程中存储临时数据。
  • 用户内存:它主要用于存储RDD转换操作所需的数据, 例如RDD依赖项的信息。
  • 保留的内存:内存是为系统保留的, 用于存储Spark的内部对象。
内存分配如下所示:
深入了解Spark内存管理模型

文章图片
3.2.2。堆外模型 Spark 1.6开始引入堆外内存(SPARK-11389)。默认情况下, 堆外内存是禁用的, 但是我们可以通过spark.memory.offHeap.enabled参数启用它, 并通过spark.memory.offHeap.size参数设置内存大小。与堆上内存相比, 堆外内存的模型相对简单, 仅包括存储内存和执行内存, 其分布如下图所示:
深入了解Spark内存管理模型

文章图片
如果启用了堆外内存, 则执行器中将同时有堆上内存和堆外内存。此时, 执行器中的执行内存是堆内部的执行内存与堆外部的执行内存之和。存储内存也是如此。下图显示了Spark堆内部和外部的堆上和堆外内存。
深入了解Spark内存管理模型

文章图片
3.2.3。动态占用机制
  • 提交程序后, 将根据spark.memory.storageFraction参数设置存储存储区和执行存储区。
  • 当程序运行时, 如果双方的空间不足(存储空间不足以放置一个完整的块), 它将根据LRU存储到磁盘;如果其中一个空间不足, 而另一个空间可用, 则它将借用另一个空间。
  • 存储占用对方的内存, 然后将占用的部分转移到硬盘上, 然后“返还”借来的空间。
  • 执行占用了对方的内存, 并且在当前实现中无法“返回”借用的空间。因为由Shuffle进程生成的文件将在以后使用, 并且以后不必再使用Cache中的数据, 所以返回内存可能会导致严重的性能下降。

    推荐阅读