本课主题

  • Static MemoryManager 的源码鉴赏  
  • Unified MemoryManager 的源码鉴赏

引言

从源码的角度了解 Spark 内存管理是怎么设计的,从而知道应该配置那个参数让程序运行更适合你的实际需要,我们为什么要把 Spark Memory 这块内存调大,原因很简单,理论上讲你调得愈来,你占用的空间愈大,程序运行时所产生的 IO 就会愈来愈少,理论可以参考第四章 : Spark 中 JVM 内存使用及配置内幕详情。这一章是对于理论的源码补充!希望这篇文章能为读者带出以下的启发:

  • 了解 MemoryManger: Unified Memory Manager, Static Memory Manager 以及它们的核心功能与方法
  • 了解 MemoryPool: StorageMemoryPool 和 ExecutionMemoryPool 以及它们的核心功能与方法

Spark 2.1.0 中两种内存管理的源码剖析

回顾上一章所讲的 Spark Shuffle 内存分配理论,我们知道 Spark Shuffle 的内存管理有两种:一种是联合内存管理器 (Spark Unified Memory)、一种是静态内存管理器 (Spark Static Memory),首先这两个类都是继承著 MemoryManager,MemoryManager 是一个抽象类,它这样设计很容易理解呀,我们应用程序中使用接口就是为了包容未来的变化,因为现在只有两个内存管理器,将来可能会有好几种内存控制器。

MemoryManager 主要有几个功能:

  • 记录用了多少 StorageMemory 和 ExecutionMemory
  • 申请 Storage、Execution 和 Unroll Memory:acquireStorageMemory, acquireExectionMemory, acquireUnrollMemory
  • 释放 Storage 和 Execution Memory

抽象类 MemoryManager

  1. 首先看看 MemoryManger ,它会强制管理储存(Storage)和执行(Execution)之间的内存使用,从 MemoryManager 申请可以把剩馀空间借给对方。所有 Task 的运行就是 ShuffleTask 的运行,ExecutionMemory是指 Shuffles,joins,sorts 和 aggregation 的操作;而 StorageMemory 是缓存和广播数据相关的,每一个 JVM 会产生一个 MemoryManager 来负责管理内存。MemoryManager 构造时,需要指定 onHeapStorageMemeory和 onHeapExecutionMemory 的参数。
    [下图是 MemoryManager.scala 中 抽象类 MemoryManager 接收的参数]
  2. 在 MemoryManager 对象构造的时候创建 StorageMemoryPool 和 ExecutionMemoryPool 对象,用来管理了 Storage 和 Execution 的内存分配。
    [下图是 MemoryManager.scala 中 OnHeapStorageMemoryPool, OffHeapStorageMemoryPool, OnHeapExecutionMemoryPool, OffHeapExecutionMemoryPool 变量]

    这里是 StorageMemory 用来记录 Storage 使用了多少内存
    [下图是 StorageMemoryPool.scala 中 memoryUsed 方法]

    这里是 ExecutionMemory 用来记录 Execution 使用了多少内存,它创建一些 HashMap 来存储每个 Task 的内存使用量,把 Map 中的所有 Value 加起来便用当前 ExecutionMemory 的总使用量。
    [下图是 ExecutionMemoryPool.scala 中 memoryUsed 方法]
  3. MemoryStore 也是被 BlockManager 管理的,以下是其中一个 MemoryStore 调用 acquireStorageMemory 方法的源代码,一个 Block 怎么实代化可以参考 [Spark内核] 第38课:BlockManager架构原理、运行流程图和源码解密[下图是 MemoryStore.scala 中 putBytes 方法]
  4. 现在 Spark 2.1 默认的 MemoryManager 是 UnifiedMemoryManager,你可以看到下里有一段条件判断的逻辑,如果 spark.memory.userLegacyMode 是 true 的话,MemeoryManager 便是 StaticMemoryManager,否则的话就是 Spark Unified Memory。
    [下图是 SparkEnv.scala 中 memoryManager 变量]
  5. 在 MemoryManager 中有一个很关键的代码,如果你想使用 OffHeap 作为储存的话,你必需设置 spark.memory.offHeap.enabled 为 true,还有确定你的 offHeap 系统的空间必须大于 0。
    [下图是 MemoryManager.scala 中 tungstenMemoryMode 变量]

Static MemoryManager 的源码鉴赏

  1. 默认空间的计算方式
    [下图是 StaticMemoryManager.scala 中 getMaxStorageMemory 方法]

    [下图是 StaticMemoryManager.scala 中 maxUnrollMemory 变量]
  2. acquireStorageMemory
    [下图是 StaticMemoryManager.scala 中 acquireStorageMemory 方法]
  3. acquireExecutionMemory
    [下图是 StaticMemoryManager.scala 中 acquireExecutionMemory 方法]
  4. acquireUnrollMemory
    [下图是 StaticMemoryManager.scala 中 acquireUnrollMemory 方法]

Unified MemoryManager 的源码鉴赏

  1. UnifiedMemoryManager 构造时调用工厂方法 apply( ),默认是把 Storage空间的50%给 Execution
    [下图是 UnifiedMemoryManager.scala 中 UnifiedMemoryManager 构造参数]

    你可以很清楚的看见:默认的 Reserved System Memory 是 300M,然后默认的 HeapStorageRegionSize 是 MaxMemory x 50%,如果实现了 OffHeapExecutionMemoryPool 你觉得会不会有从 StorageMemory 获得储存这个概念? 实际上不需要找 Storage 借空间。如果是 ShuffleTask 计算比较复杂的情况,使用 Unified Memory Management 会取得更好的效率,但是如果说计算的业务逻辑需要更大的缓存空间,此时使用 StaticMemoryManagement 效果会更好。
    [下图是 UnifiedMemoryManager.scala 中 UnifiedMemoryManager 伴生对象里的 RESERVED_SYSTEM_MEMORY_BYTES 参数和 apply 方法]

    [下图是 UnifiedMemoryManager.scala 中 UnifiedMemoryManager 伴生对象里的 getMaxMemory 方法]
  2. 上一章讨论过在 Unified 机制下有两种方法 Execution 会向 Storage 借空间,现在配合源码来证明这个说法。Unified Memory Manager 有两个核心方法,第一个是 acquiredExecutionMemeory acquireStorageMemory,当 ExecutionMemory 有剩馀空间时可以借给 StorageMemory,然后通过调用 StorageMemoryPool 的 acquireMemory 方法向 storageMemoryPool 申请空间。
    [下图是 UnifiedMemoryManager.scala 中 acquireStorageMemory 方法]

    [下图是 ExecutionMemoryPool.scala 中 acquireMemory 方法]
  3. acquiredExecutionMemory 主要是为当前的执行任务去获得的执行空间,它首先会根据我们的 onHeap 和 offHeap 这两种不同的方式来进行配。
    [下图是 UnifiedMemoryManager.scala 中 acquireExecutionMemory 方法]
  4. 在MemoryManager 构造的时候也分配一定的内存空间 poolSize
    [下图是 MemoryManager.scala 中调用了 incremenPoolSize 方法]

    [下图是 MemoryPool.scala 中 incremenPoolSize 方法]
  5. 调用 computeMaxExecutionPoolSize 方法向 ExecutionPool 申请资源。过程中会调用 maybeGrowExecutionPool来判断需要多少内存,包括计算内存空间的空闲资源与Storage曾经占用的空间。
    [下图是 UnifiedMemoryManager.scala 中 computeMaxExecutionPoolSize 方法]

    maybeGrowExecutionPool 方法会首先判断申请的内存申请资源是大于0,然后判断是剩馀空间和 Storage曾经占用的空间多,把需要的内存资源量提交给 StorageMemoryPool 的 freeSpaceToShrinkPool 方法。
    [下图是 UnifiedMemoryManager.scala 中 maybeGrowExecutionPool 方法]

    然后会判断是当前 FreeSpace 能不能满足 Execution 的需要,如果无法满足则调用 MemoryStore的evictVlocksToFreeSpace方法在 StorageMemoryPool 中挤掉一部份数据。
    [下图是 StorageMemoryPool.scala 中 freeSpaceToShrinkPool 方法]
  6. 调用 ExecutionPool 的 acquireMemory 方法向 ExecutionPool 申请内存资源,每个 Task 理论上讲一般能使用的大小是从 poolSize /(2 x numActiveTasks) 到 maxPoolSize/numActiveTasks
    [下图是 ExecutionMemoryPool.scala 中 acquireMemory 方法]

參考資料

资料来源来至 DT大数据梦工厂 大数据商业案例以及性能调优 
第31课:彻底解密Spark 2.1.X中Shuffle中内存管理源码解密:StaticMemory和UnifiedMemory

Spark源码图片取自于 Spark 2.1.0版本

[Spark性能调优] 源码补充 : Spark 2.1.X 中 Unified 和 Static MemoryManager的更多相关文章

  1. [Spark性能调优] 第四章 : Spark Shuffle 中 JVM 内存使用及配置内幕详情

    本课主题 JVM 內存使用架构剖析 Spark 1.6.x 和 Spark 2.x 的 JVM 剖析 Spark 1.6.x 以前 on Yarn 计算内存使用案例 Spark Unified Mem ...

  2. [Spark性能调优] 第三章 : Spark 2.1.0 中 Sort-Based Shuffle 产生的内幕

    本課主題 Sorted-Based Shuffle 的诞生和介绍 Shuffle 中六大令人费解的问题 Sorted-Based Shuffle 的排序和源码鉴赏 Shuffle 在运行时的内存管理 ...

  3. spark性能调优(四) spark shuffle中JVM内存使用及配置内幕详情

    转载:http://www.cnblogs.com/jcchoiling/p/6494652.html 引言 Spark 从1.6.x 开始对 JVM 的内存使用作出了一种全新的改变,Spark 1. ...

  4. Spark性能调优之道——解决Spark数据倾斜(Data Skew)的N种姿势

    原文:http://blog.csdn.net/tanglizhe1105/article/details/51050974 背景 很多使用Spark的朋友很想知道rdd里的元素是怎么存储的,它们占用 ...

  5. [Spark性能调优] 第一章:性能调优的本质、Spark资源使用原理和调优要点分析

    本課主題 大数据性能调优的本质 Spark 性能调优要点分析 Spark 资源使用原理流程 Spark 资源调优最佳实战 Spark 更高性能的算子 引言 我们谈大数据性能调优,到底在谈什么,它的本质 ...

  6. Spark性能调优

    Spark性能优化指南——基础篇 https://tech.meituan.com/spark-tuning-basic.html Spark性能优化指南——高级篇 https://tech.meit ...

  7. 性能调优的本质、Spark资源使用原理和调优要点分析

    本课主题 大数据性能调优的本质 Spark 性能调优要点分析 Spark 资源使用原理流程 Spark 资源调优最佳实战 Spark 更高性能的算子 引言 我们谈大数据性能调优,到底在谈什么,它的本质 ...

  8. Spark性能调优之JVM调优

    Spark性能调优之JVM调优 通过一张图让你明白以下四个问题                1.JVM GC机制,堆内存的组成                2.Spark的调优为什么会和JVM的调 ...

  9. Spark性能调优之代码方面的优化

    Spark性能调优之代码方面的优化 1.避免创建重复的RDD     对性能没有问题,但会造成代码混乱   2.尽可能复用同一个RDD,减少产生RDD的个数   3.对多次使用的RDD进行持久化(ca ...

随机推荐

  1. 使用java类破解MyEclipse

    今天在网上查资料的时候无意中发现使用java类破解MyEclipse的注册码问题.跟大家分享一下 1.建立JAVA Project,随便命名,只要符合规则就行 2.在刚刚建好的Project右击src ...

  2. Git~GitLab当它是一个CI工具时

    CI我们都知道,它是持续集成的意思,主要可以自动处理包括编译,测试,发布等一系列的过程,而GitLab也同样包含了这些功能,我们可以通过pipeline很容易的实现一个软件从编译,测试,发布的自动化过 ...

  3. NBUT 1217 Dinner

    [1217] Dinner 时间限制: 1000 ms 内存限制: 32768 K 问题描写叙述 Little A is one member of ACM team. He had just won ...

  4. poj 2034 Anti-prime Sequences(dfs)

    //相邻的 2.3......d 之和都要不为素数 # include <algorithm> # include <stdio.h> using namespace std; ...

  5. 自学Python2.7-collections系列

    Python collections系列 Python拥有一些内置的数据类型,比如str, int, list, tuple, dict等, collections模块在这些内置数据类型的基础上,提供 ...

  6. redis的list类型

    1.简单介绍 redis的list类型其实就是一个每个元素都是string类型的双向链表.所以lpush.rpush.lpop和rpop命令的时间复杂度是O(1),list会记录链表的长度,所以lle ...

  7. Not++规范格式(格式化)

    Notepad++功能比Windows中的 Notepad(记事本)强大,除了可以用来制作一般的纯文字说明文件,也十分适合编写计算机程序代码.Notepad++ 有语法高亮度显示和语法折叠功能,并且支 ...

  8. 转换函数TO_CHAR,TO_DATE,TO_NUMBER

    TO_CHAR:将日期.数字转为字符串. TO_DATE:将字符串转为日期(注:无数字转日期). TO_NUMBER:将字符串转为数字(注:无日期转数字).此函数作用不大,算术运算时Oracel会自动 ...

  9. 那些容易遗忘的web前端问题

    背景: 年底将至,本人这只才出门的前端菜鸟,终于有空闲的时间来整理一下最近投简历时出现的问题.有的是经常使用但是没有仔细留意造成的:有的是个人认为根本没人使用而忽略的.为了下次不出现这种错误,进行一下 ...

  10. Angular 报错 Can't bind to 'formGroup' since it isn't a known property of 'form'

    错误描述 当form表单加FormGroup属性时报错 Can't bind to 'formGroup' since it isn't a known property of 'form' < ...