spark统一内存模型 详解

        Apache Spark 是一个用于大规模数据处理的分布式计算框架,它支持多种处理模型(如批处理、流处理、SQL、机器学习等)。为了高效地在分布式环境中处理这些多样化的工作负载,Spark 在 2.x 版本后引入了统一内存管理模型,以便在不同类型的计算和存储任务之间合理分配和管理内存。

        本文将详细全面地从底层原理和部分源代码的角度解释 Spark 的统一内存模型,涵盖其内存管理的基本思想、不同的内存区域划分、动态内存管理机制以及具体的内存分配和回收机制。

1. Spark 的内存管理问题

        Spark 处理大量数据时,内存是一个关键的资源。传统的内存管理模型(Spark 1.x)中,内存资源主要被划分为两部分:

  1. 存储内存(Storage Memory):用于缓存中间计算结果(如 RDD Cache 或 Broadcast 变量)。
  2. 执行内存(Execution Memory):用于执行任务时的数据操作(如 shuffle、join、sort 时的数据缓冲区)。

        在 1.x 版本中,这两部分内存是彼此隔离的,存储内存和执行内存之间的使用是静态分配的。如果一部分内存不足,而另一部分有多余内存,无法进行灵活共享。这个问题在 2.x 版本中得到了改进,引入了统一内存管理模型

2. 统一内存管理模型的基本思想

        在 Spark 2.x 版本中(其实是1.6以后就出现了),内存模型的核心思想是通过动态调整存储内存和执行内存之间的划分,使得内存资源在运行时能够根据实际需要进行分配。这个动态分配机制使得在某些场景下(如缓存使用较少或执行任务不密集时),存储内存和执行内存可以灵活地共享内存资源。

统一内存模型主要有两个核心区域:

  1. 堆内内存(On-heap Memory):通过 JVM 堆来管理的内存,用于存储和操作数据。
  2. 堆外内存(Off-heap Memory):不在 JVM 堆中管理的内存,通常通过 sun.misc.Unsafe 或者直接的操作系统调用进行分配和管理,用于减少 JVM 垃圾回收(GC)的影响。

统一内存管理的核心在内存区域的动态占用机制,其占用规则如下:

  • 双方空间都不足时,则存储到硬盘;如己方空间不足而对方空余时,可借用对方的空间;(存储空间不足是指不足以放下一个完整的 Block)。
  • 执行内存的空间被对方占用后,可让对方将占用的部分存储转存到硬盘,然后“归还”借用的空间。
  • 存储内存的空间被对方占用后,无法让对方“归还”,因为需要考虑到 Shuffle 过程中很多因素,实现起来较为复杂。

Spark 的内存管理通过两个子模块进行控制:

  • 静态内存管理(Static Memory Management):用户根据应用程序需求预定义内存分配策略,Spark 不会动态调整分配。
     
  • 动态内存管理(Dynamic Memory Management):Spark 动态调整内存的使用以提高资源利用率。

3. Spark 内存的核心划分

在 Spark 中,内存被分为如下几个区域:

  1. Reserved Memory(保留内存):这部分内存用于 Spark 内部一些核心的操作,如内存管理、任务调度等。通常是一个固定的小比例,默认情况下保留 300MB。

  2. User Memory(用户内存):这部分内存用于存放用户数据结构、内存中的对象等。主要用来执行非 Spark 任务本身的数据操作(如用户自定义的代码)。

  3. Execution Memory(执行内存):用于执行任务时所需的内存,如进行 shuffle、join、sort 等操作时的数据缓冲区。

  4. Storage Memory(存储内存):用于缓存 RDD 的中间计算结果、广播变量等。它可以通过 persist 或 cache 方法将数据保存在内存中,以便重用。

4. 动态内存管理机制

        Spark 的统一内存管理模型采用动态内存管理机制,允许 Execution Memory 和 Storage Memory 在一定条件下共享内存资源。当 Execution Memory 或 Storage Memory 的使用量较低时,未被使用的部分可以被另一方临时使用。

4.1 动态分配策略

动态分配策略的核心机制体现在如下几点:

  1. 共享机制Execution Memory 和 Storage Memory 在需要时可以动态调整各自的内存占用,但两者总内存使用不会超过可用内存的最大限制(spark.memory.fraction,默认为 0.75,即 JVM 堆内存的 75%)。
  2. 逐步收回:当 Execution Memory 需要更多内存时,Spark 会首先尝试从 Storage Memory 中回收未使用的缓存空间。如果缓存的数据占满了存储内存且不能被回收,任务执行可能会出现内存不足。
  3. 溢出磁盘:当 Execution Memory 或 Storage Memory 超过了指定的内存限制时,Spark 会将部分数据溢出到磁盘以保证内存的有效使用。

5. Spark 统一内存模型的源代码解析

        接下来,我们深入解析 Spark 的内存管理相关的核心源代码,了解其底层实现。

5.1 UnifiedMemoryManager(统一内存管理器)

        UnifiedMemoryManager 是 Spark 内部管理内存的核心类。它负责跟踪和分配 Execution Memory 和 Storage Memory,并根据内存使用情况动态调整内存划分。

class UnifiedMemoryManager(override val maxHeapMemory: Long,memoryFraction: Double,storageRegionSize: Long,onHeapStorageMemory: Long,offHeapStorageMemory: Long) extends MemoryManager {// 计算执行内存的最大限制,基于 memoryFraction 参数private val maxExecutionMemory = (maxHeapMemory * memoryFraction).toLong// 当前已分配的执行内存private var executionMemoryUsed = 0L// 当前已分配的存储内存private var storageMemoryUsed = 0L// 获取执行内存的接口override def acquireExecutionMemory(numBytes: Long,taskAttemptId: Long,memoryMode: MemoryMode): Long = {val availableExecutionMemory = maxExecutionMemory - executionMemoryUsedval memoryToAcquire = math.min(numBytes, availableExecutionMemory)executionMemoryUsed += memoryToAcquirememoryToAcquire}// 获取存储内存的接口override def acquireStorageMemory(blockId: BlockId,numBytes: Long,memoryMode: MemoryMode): Boolean = {val availableStorageMemory = maxStorageMemory - storageMemoryUsedif (availableStorageMemory >= numBytes) {storageMemoryUsed += numBytestrue} else {false}}
}

在这个类中:

  • maxExecutionMemory:表示执行内存的最大限制,基于 memoryFraction 参数计算得出。
  • acquireExecutionMemory:负责从执行内存中分配指定数量的内存。如果当前执行内存不足,Spark 会根据内存使用情况尝试回收存储内存。
  • acquireStorageMemory:负责为存储缓存(如 RDD Cache)分配内存。如果当前的存储内存不足,Spark 会首先尝试从执行内存中获取未使用的部分。
5.2 动态调整机制

        Spark 的内存管理器能够动态地调整执行内存和存储内存之间的分配。通过以下两个方法来实现动态调整:

  • executionMemoryUsed:记录当前执行任务已经使用的执行内存。当执行任务完成后,内存会被释放并归还给内存池。
  • storageMemoryUsed:记录当前用于缓存数据的存储内存。当存储的 RDD 被移除或者被淘汰时,内存会被释放。

        当 Execution Memory 需要更多内存时,acquireExecutionMemory 会检查 Storage Memory 是否有未使用的部分,然后回收这些内存。

5.3 内存的申请与释放

内存的申请和释放是通过以下两个核心方法实现的:

  • 申请内存:在 acquireExecutionMemory 或 acquireStorageMemory 中,系统根据当前的内存使用情况分配内存,并调整 executionMemoryUsed 和 storageMemoryUsed
  • 释放内存:当任务执行完成或缓存不再需要时,通过 releaseExecutionMemory 或 releaseStorageMemory 将内存归还给系统。
def releaseExecutionMemory(numBytes: Long, taskAttemptId: Long, memoryMode: MemoryMode): Unit = {executionMemoryUsed -= numBytes
}def releaseStorageMemory(numBytes: Long, blockId: BlockId, memoryMode: MemoryMode): Unit = {storageMemoryUsed -= numBytes
}

5.4 内存的动态扩展

        当 Execution Memory 或 Storage Memory 无法满足需求时,Spark 会尝试动态扩展内存的使用。MemoryManager 会检查其他内存池是否有未使用的内存,如果有,则可以临时借用部分内存。

        例如,在 acquireExecutionMemory 中,Spark 会首先检查是否有足够的执行内存,如果不足,则会从存储内存中回收未使用的部分:

val memoryToBorrow = math.min(availableStorageMemory, numBytes - availableExecutionMemory)
executionMemoryUsed += memoryToBorrow

        这种机制保证了 Spark 在内存不足时,能够尽量通过动态扩展来提高内存的利用率,避免因内存不足而导致任务失败。

6. 堆外内存管理

        Spark 还支持堆外内存(Off-heap Memory)的管理,主要用于减少 JVM 垃圾回收的开销。在堆外内存模式下,Spark 会绕过 JVM 堆,通过操作系统直接分配和管理内存。

        堆外内存的管理通过 sun.misc.Unsafe 或者 Netty 框架来实现,具体机制与堆内内存管理类似,不过它的内存分配不受 JVM 堆限制,因此能够在某些场景下提供更高的性能。

        用户可以通过配置 spark.memory.offHeap.enabled 参数启用堆外内存管理,同时设置 spark.memory.offHeap.size 来指定堆外内存的大小。

7. 内存回收与垃圾回收

        Spark 的内存回收机制与 JVM 的垃圾回收机制密切相关。当内存管理器检测到内存不足时,Spark 会尝试触发垃圾回收(GC),以回收未使用的对象和内存。

        Spark 内存管理器与 GC 结合紧密,特别是当执行任务时,临时对象会频繁创建并在任务结束后被回收。因此,适当的 GC 策略(如 G1、CMS)对于 Spark 应用的性能至关重要。

        Spark 还提供了多种 GC 调优选项,用户可以通过调整 JVM 参数(如 -Xmx-XX:MaxGCPauseMillis)和 Spark 参数(如 spark.memory.fractionspark.memory.storageFraction)来优化内存使用和垃圾回收。

总结

        Spark 的统一内存模型通过动态调整执行内存和存储内存的划分,极大地提高了内存资源的利用率。通过引入堆外内存支持、灵活的内存共享机制以及动态扩展策略,Spark 能够在不同类型的任务(如批处理、流处理、机器学习)之间高效地分配和管理内存资源。

        我们从底层原理和源代码的角度详细解析了 Spark 内存管理的工作机制,了解了 UnifiedMemoryManager 如何动态管理和调度内存,以及内存的申请、释放与回收机制。掌握这些底层实现细节有助于在实际应用中优化 Spark 性能,提升资源利用率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/56451.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【实战项目】——Boost搜索引擎(五万字)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、项目的相关背景 1.1、什么是Boost库? 1.2、什么是搜索引擎? 1.3、为什么要做Boost库搜索引擎? 二、搜索引擎的宏观原…

生产小工单如何轻松实现生产任务敏捷管理的?

在现代生产任务制造业中,有效管理生产是提升效率的关键。生产管理系统中的生产小工单管理,是解决传统生产管理痛点的重要工具。本文将深入探讨生产小工单的概念、流程及其主要功能。 下面我会用一个实际的生产工单系统为例,给大家详细介绍下…

苍穹外卖--开发记录day12(完结篇)

目录 苍穹外卖day12一:工作台二:apachePOI三:导出excel报表(最后一个功能!!) 总结(项目完结心得) 苍穹外卖day12 一:工作台 代码导入之后就是这样的效果&…

Java 虚拟机(JVM)中的内存泄漏排查技巧及各种内存查看命令分析工具推荐

文章目录 引言什么是内存泄漏?工具和技术1. 使用 jstat 监控 JVM2. 使用 jmap 生成堆转储文件3. 使用 jvisualvm 分析堆转储文件4. 使用 MAT(Memory Analyzer Tool)5. 使用 YourKit 或 JProfiler6. 代码审查和静态分析 实战案例案例 1&#x…

使用U-KAN训练自己的数据集 — 医疗影像分割

<U-KAN Makes Strong Backbone for Medical Image Segmentation and Generation> U-Net已成为各种视觉应用的基石,如图像分割和扩散概率模型。虽然通过整合变压器或mlp引入了许多创新设计和改进,但网络仍然局限于线性建模模式以及缺乏可解释性。为了应对这些挑战,受到…

ChartCheck: Explainable Fact-Checking over Real-World Chart Images

论文地址: https://aclanthology.org/2024.findings-acl.828.pdfhttps://aclanthology.org/2024.findings-acl.828.pdf 1.概述 事实验证技术在自然语言处理领域获得了广泛关注,尤其是在针对误导性陈述的检查方面。然而,利用图表等数据可视化来传播信息误导的情况却很少受到…

【简历】25届浙江某211大学JAVA简历:明明项目有货,但是长篇大论减分!!

注&#xff1a;为保证用户信息安全&#xff0c;姓名和学校等信息已经进行同层次变更&#xff0c;内容部分细节也进行了部分隐藏 另外&#xff1a;我们出这一系列校招简历指导的原因&#xff0c;就是看很多学生被忽悠&#xff0c;没有先定位大厂、中厂还是小公司&#xff0c;导…

Logistic回归(分类)问题探讨与实践

说明 在本专栏机器学习_墨#≯的博客-CSDN博客之前的博文中&#xff0c;已经对感知机[1]、SVM[2]以及线性回归问题[3]做过探讨和实践。感知机以及SVM是用来做分类的&#xff0c;线性回归是用来做拟合的。本文将要探讨的Logistic回归(逻辑回归)则是使用线性回归的方法来做分类(二…

90V转5V4A同步降压芯片WT6037

90V转5V4A同步降压芯片WT6037 WT6037 被定义为一款高压同步降压转换器&#xff0c;其设计可在 10V 至 90V 的宽泛工作电压区间内稳定运行。该转换器尤其适用于需承受宽电压输入范围的电池组系统&#xff0c;诸如 12V 至 72V 的电池组&#xff0c;以及 60V 至 90V 的降压应用场…

DOL 电机启动器接触器选择和选型

接触器连接在断路器和热过载保护继电器之间。接触器的选型和选择过程与断路器在计算满载电流和计算额定电流方面的过程类似。不同之处在于市场上可用组件的选择表。 从断路器的选型和选型中&#xff0c;我们发现&#xff1a; 电机尺寸 6HP 功率(P)4476W 电机FLC7.78A 断路…

汽车免拆诊断案例 | 2019 款奥迪 A6L 车行驶中偶发熄火

故障现象  一辆2019款奥迪A6L车&#xff0c;搭载2.0T发动机&#xff0c;累计行驶里程约为9万km。车主反映&#xff0c;车辆行驶中偶发熄火&#xff0c;故障频率较高。 故障诊断  接车后试车&#xff0c;起动发动机&#xff0c;可以正常起动着机。使用故障检测仪检测&#x…

一起搭WPF架构之完结总结篇

一起搭WPF架构之完结总结篇 前言设计总结设计介绍页面一页面二页面三 结束 前言 整体基于WPF架构&#xff0c;根据自己的需求简单设计与实现了衣橱的数据统计、增加与读取数据、并展示数据的小软件。我知道自己在设计方面还有很多不足&#xff0c;暂时先做到这里了&#xff0c…

【SQL|大数据|数据清洗|过滤】where条件中 “ != “ 和 “ NOT IN() ” 对NULL的处理

对数据进行清洗过滤的时候&#xff0c;NULL往往是一个很特殊的存在&#xff0c;对NULL值的存在通常有以下三种方式 1、保留NULL 2、过滤掉NULL 3、将NULL替换为其他符合业务需求的默认常量 下面是一些常用处理NULL的方式&#xff1a; 如下图所示数据源&#xff1a; car_vin&…

天锐绿盾与Ping32内网安全保护能力对比,选择最优方案

在数字化时代&#xff0c;企业内网安全面临着越来越多的挑战。有效保护内网数据安全&#xff0c;防止信息泄露和外部攻击至关重要。今天&#xff0c;我们将对比天锐绿盾和Ping32这两款内网安全保护软件&#xff0c;帮助您选择最适合的解决方案。 1. 安全防护机制 Ping32 Ping…

熔解曲线技术路线简介

在实时荧光定量PCR&#xff08;qPCR&#xff09;实验中&#xff0c;当双链DNA受热时&#xff0c;其互补碱基之间的氢键会逐渐断裂&#xff0c;导致双链分离成两条单链&#xff0c;这一过程被称为DNA的“熔解”。 总的DNA双螺旋结构降解一半的温度称为熔解温度&#xff08;Tm&a…

游戏推荐业务中基于 sentinel 的动态限流实践

作者&#xff1a;来自 vivo 互联网服务器团队- Gao Meng 本文介绍了一种基于 sentinel 进行二次开发的动态限流解决方案&#xff0c;包括什么是动态限流、为什么需要引入动态限流、以及动态限流的实现原理。 一、背景 1.1 当前的限流方案 随着互联网的发展及业务的增长&…

python基础综合案例(数据可视化-地图可视化)

1.基础地图使用 注意写名字的时候要写全名&#xff0c;比如上海市不能写出上海&#xff0c;不然看不到数据 鼠标点击即可看到数据 设置属性的时候不要忘记导包 # 演示地图可视化的基础使用 from pyecharts.charts import Map from pyecharts.options import VisualMapOpts # 准…

MongoDB安装配置及配置和启动服务

MongoDB 安装配置 附&#xff1a;MongoDB官网下载地址&#xff1a; https://www.mongodb.com/download-center/community 注&#xff1a; 官网可以下载最新版的MongoDB安装包&#xff0c;有MSI安装版和ZIP安装版。我们课堂上使用4.4.4的ZIP安装版。安装版参考博客&#xff1…

JavaScript 在网页设计中的四大精彩案例:画布时钟、自动轮播图、表单验证与可拖动元素

在网页开发中&#xff0c;JavaScript 发挥着至关重要的作用&#xff0c;为网页带来丰富的交互性和动态效果&#xff0c;极大地提升了用户体验。本文将通过几个具体案例展示 JavaScript 的强大魅力。 一、美丽的画布时钟 这是一个使用 JavaScript 在网页上创建美丽画布时钟的案…

Pytorch Note

cat函数: cat函数不会增加维度&#xff0c;默认按照dim0连接张量 stack函数: stack函数会增加一个维度 nn.Linear的默认输入: torch中默认输入一定要为tensor&#xff0c;并且默认是tensor.float32&#xff0c;此外device如果没有model.to(device)放到gpu上面默认会在cpu上运…