Spark 1.6 内存管理模型( Unified Memory Management)分析

2016年1月4号 Spark 1.6 发布。提出了一个新的内存管理模型: Unified Memory Management。这篇文章会详细分析新的内存管理模型,方便大家做调优。

前言

新的内存模型是在这个Jira提出的,JIRA-10000,对应的设计文档在这:unified-memory-management

贴出这个文档是为了让大家可以更深入的了解这个新的模型的设计动机,社区想到的解决方案,以及经过对比,最终选择了哪个方案。当然我在文章中也会有所提及,但这个不会是本文的重点。

Memory Manager

在Spark 1.6 版本中,memoryManager 的选择是由

 spark.memory.useLegacyMode=false

决定的。如果采用1.6之前的模型,这会使用StaticMemoryManager来管理,否则使用新的UnifiedMemoryManager

我们先看看1.6之前,对于一个Executor,内存都有哪些部分构成:

  1. ExecutionMemory。这片内存区域是为了解决 shuffles,joins, sorts and aggregations 过程中为了避免频繁IO需要的buffer。 通过spark.shuffle.memoryFraction(默认 0.2) 配置。

  2. StorageMemory。这片内存区域是为了解决 block cache(就是你显示调用dd.cache, rdd.persist等方法), 还有就是broadcasts,以及task results的存储。可以通过参数 spark.storage.memoryFraction(默认0.6)。设置

  3. OtherMemory。给系统预留的,因为程序本身运行也是需要内存的。 ​(默认为0.2).​

另外,为了防止OOM,一般而言都会有个safetyFraction,比如ExecutionMemory 真正的可用内存是 spark.shuffle.memoryFraction * spark.shuffle.safetyFraction 也就是0.8 * 0.2 ,只有16%的内存可用。
这种内存分配机制,最大的问题是,谁都不能超过自己的上限,规定了是多少就是多少,虽然另外一片内存闲着呢。这在是StorageMemory 和 ExecutionMemory比较严重,他们都是消耗内存的大户。

这个问题引出了Unified Memory Management模型,重点是打破ExecutionMemory 和 StorageMemory 这种分明的界限。

OtherMemory

Other memory在1.6也做了调整,保证至少有300m可用。你也可以手动设置 spark.testing.reservedMemory . 然后把实际可用内存减去这个reservedMemory得到 usableMemory。 ExecutionMemory 和 StorageMemory 会共享usableMemory * 0.75的内存。0.75可以通过 新参数 spark.memory.fraction 设置。目前spark.memory.storageFraction 默认值是0.5,所以ExecutionMemory,StorageMemory默认情况是均分上面提到的可用内存的。

UnifiedMemoryManager

这个类提供了两个核心的方法:

acquireExecutionMemory 
acquireStorageMemory

acquireExecutionMemory

每次申请ExecutionMemory 的时候,都会调用 maybeGrowExecutionPool方法,基于该方法我们可以得到几个有意义的结论:

  • 如果ExecutionMemory 内存充足,则不会触发向Storage申请内存
  • 每个Task能够被使用的内存被限制在 poolSize / (2 * numActiveTasks) ~ maxPoolSize / numActiveTasks 之间。
maxPoolSize = maxMemory - math.min(storageMemoryUsed, storageRegionSize)

poolSize = ExecutionMemoryPool.poolSize (当前ExecutionMemoryPool 所持有的内存)
  • 如果ExecutionMemory 的内存不足,则会触发向StorageMemory索引要内存的操作。

如果ExecutionMemory 的内存不足,则会向 StorageMemory要内存,具体怎么样呢? 看下面一句代码就懂了:

val memoryReclaimableFromStorage =  math.max(storageMemoryPool.memoryFree, storageMemoryPool.poolSize - storageRegionSize)

看StorageMemoryPool的剩余内存和 storageMemoryPool 从ExecutionMemory借来的内存那个大,取最大的那个,作为可以重新归还的最大内存。用公式表达出来就是这一个样子:

ExecutionMemory 能借到的最大内存= StorageMemory 借的内存 + StorageMemory 空闲内存

当然,如果实际需要的小于能够借到的最大值,则以实际需要值为准。下面的代码体现了这个逻辑:

val spaceReclaimed = storageMemoryPool.shrinkPoolToFreeSpace(  
      math.min(extraMemoryNeeded,memoryReclaimableFromStorage))

onHeapExecutionMemoryPool.incrementPoolSize(spaceReclaimed)

acquireStorageMemory

流程和acquireExecutionMemory类似,但是区别是,当且仅当ExecutionMemory有空闲内存时,StorageMemory 才能借走该内存。这个逻辑体现在这行代码上:

 val memoryBorrowedFromExecution = Math.min(onHeapExecutionMemoryPool.memoryFree, numBytes)

所以StorageMemory从ExecutionMemory借走的内存,完全取决于当时ExecutionMemory是不是有空闲内存。

MemoryPool

前面讲的是StorageMemory和ExecutionMemory的交互。现在内存的具体表示则是由 MemoryPool完成的。

UnifiedMemoryManage 维护了三个对象:

@GuardedBy("this")
  protected val storageMemoryPool = new StorageMemoryPool(this)
  @GuardedBy("this")
  protected val onHeapExecutionMemoryPool = new ExecutionMemoryPool(this, "on-heap execution")
  @GuardedBy("this")
  protected val offHeapExecutionMemoryPool = new ExecutionMemoryPool(this, "off-heap execution")

真实的内存计数其实都是由这几个对象来完成的。比如

  • 内存的借出借入
  • task目前内存的使用跟踪

值的注意的是,我们以前知道,系统shuffle的时候,是可以使用in-heap /off-heap 内存的。在UnifiedMemoryManage中,用了不同的对象来追踪。如果你开启的是offHeapExecutionMemoryPool,则不存在和StorageMemory的交互,也就没有动态内存的概念了。

总结

  1. 理论上可以减少Shuffle spill数,极端情况可能中间就没有spill过程了,可以大大减少IO次数
  2. 如果你的内存太紧张,可能无法缓解问题
  3. 如果你的程序具有偏向性,比如重度ExectionMemory 或者StorageMemory 的某一个,则可能会带来比较好的效果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容