【源碼剖析】- Spark 新舊內(nèi)存管理方案(下)

上一篇文章【源碼剖析】- Spark 新舊內(nèi)存管理方案(上)介紹了舊的內(nèi)存管理方案以及其實現(xiàn)類 StaticMemoryManager 是如何工作的朱盐,本文將通過介紹 UnifiedMemoryManager 來介紹新內(nèi)存管理方案(以下統(tǒng)稱為新方案)姻锁。

內(nèi)存總體分布

系統(tǒng)預(yù)留

在新方案中矿筝,內(nèi)存依然分為三塊,分別是系統(tǒng)預(yù)留渗蟹、用于 storage吟榴、用于 execution塘幅。其中系統(tǒng)預(yù)留大小如下:

val reservedMemory = conf.getLong("spark.testing.reservedMemory",
  if (conf.contains("spark.testing")) 0 else RESERVED_SYSTEM_MEMORY_BYTES)

生產(chǎn)環(huán)境中使用一般不會設(shè)置 spark.testing.reservedMemoryspark.testing骤竹,所以我們認為系統(tǒng)預(yù)留空間大小置為 RESERVED_SYSTEM_MEMORY_BYTES,即 300M俄认。

execution 和 storage 部分總大小

上一小節(jié)這段代碼是 UnifiedMemoryManager#getMaxMemory 的一個片段个少,該方法返回 execution 和 storage 可以共用的總空間,讓我們來看看這個方法的具體實現(xiàn):

  private def getMaxMemory(conf: SparkConf): Long = {
    //< 生產(chǎn)環(huán)境中一般不會設(shè)置 spark.testing.memory眯杏,所以這里認為 systemMemory 大小為 Jvm 最大可用內(nèi)存
    val systemMemory = conf.getLong("spark.testing.memory", Runtime.getRuntime.maxMemory)
    //< 系統(tǒng)預(yù)留 300M
    val reservedMemory = conf.getLong("spark.testing.reservedMemory",
      if (conf.contains("spark.testing")) 0 else RESERVED_SYSTEM_MEMORY_BYTES)
    val minSystemMemory = reservedMemory * 1.5
    //< 如果 systemMemory 小于450M夜焦,則拋異常
    if (systemMemory < minSystemMemory) {
      throw new IllegalArgumentException(s"System memory $systemMemory must " +
        s"be at least $minSystemMemory. Please use a larger heap size.")
    }
    val usableMemory = systemMemory - reservedMemory
    val memoryFraction = conf.getDouble("spark.memory.fraction", 0.75)
    //< 最終 execution 和 storage 的可用內(nèi)存之和為 (JVM最大可用內(nèi)存 - 系統(tǒng)預(yù)留內(nèi)存) * spark.memory.fraction
    (usableMemory * memoryFraction).toLong
  }

從以上代碼及注釋我們可以看出,最終 execution 和 storage 的可用內(nèi)存之和為 (JVM最大可用內(nèi)存 - 系統(tǒng)預(yù)留內(nèi)存) * spark.memory.fraction岂贩,默認為(JVM 最大可用內(nèi)存 - 300M)* 0.75茫经。舉個例子,如果你為 execution 設(shè)置了2G 內(nèi)存河闰,那么 execution 和 storage 可用的總內(nèi)存為 (2048-300)*0.75=1311

execution 和 storage 部分默認大小

上一小節(jié)搞清了用于 execution 和 storage 的內(nèi)存之和 maxMemory科平,那么用于 execution 和 storage 的內(nèi)存分別為多少呢?看下面三段代碼:

object UnifiedMemoryManager 的 apply 方法用來構(gòu)造類 UnifiedMemoryManager 的實例

  def apply(conf: SparkConf, numCores: Int): UnifiedMemoryManager = {
    val maxMemory = getMaxMemory(conf)
    new UnifiedMemoryManager(
      conf,
      maxMemory = maxMemory,
      storageRegionSize =
        (maxMemory * conf.getDouble("spark.memory.storageFraction", 0.5)).toLong,
      numCores = numCores)
  }

這段代碼確定在構(gòu)造 UnifiedMemoryManager 時:

  • maxMemory 即 execution 和 storage 能共用的內(nèi)存總和為 getMaxMemory(conf)姜性,即 (JVM最大可用內(nèi)存 - 系統(tǒng)預(yù)留內(nèi)存) * spark.memory.fraction
  • storageRegionSize 為 maxMemory * conf.getDouble("spark.memory.storageFraction", 0.5),在沒有設(shè)置 spark.memory.storageFraction 的情況下為一半的 maxMemory

那么 storageRegionSize 是干嘛用的呢髓考?繼續(xù)看 UnifiedMemoryManagerMemoryManager 構(gòu)造函數(shù):

private[spark] class UnifiedMemoryManager private[memory] (
    conf: SparkConf,
    val maxMemory: Long,
    storageRegionSize: Long,
    numCores: Int)
  extends MemoryManager(
    conf,
    numCores,
    storageRegionSize,
    maxMemory - storageRegionSize)

private[spark] abstract class MemoryManager(
    conf: SparkConf,
    numCores: Int,
    storageMemory: Long,
    onHeapExecutionMemory: Long) extends Logging

我們不難發(fā)現(xiàn):

  • storageRegionSize 就是 storageMemory部念,大小為 maxMemory * conf.getDouble("spark.memory.storageFraction", 0.5),默認為 maxMemory * 0.5
  • execution 的大小為 maxMemory - storageRegionSize,默認為 maxMemory * 0.5儡炼,即默認情況下 storageMemory 和 execution 能用的內(nèi)存相同妓湘,各占一半

互相借用內(nèi)存

新方案與舊方案最大的不同是:舊方案中 execution 和 storage 可用的內(nèi)存是固定死的,即使一方內(nèi)存不夠用而另一方有大把空閑內(nèi)存乌询,空閑一方也無法將結(jié)存借給不足一方榜贴,這樣降造成嚴重的內(nèi)存浪費。而新方案解決了這一點妹田,execution 和 storage 之間的內(nèi)存可以互相借用唬党,大大提供內(nèi)存利用率,也更好的滿足了不同資源側(cè)重的計算的需求

下面便來介紹新方案中內(nèi)存是如何互相借用的

acquireStorageMemory

先來看看 storage 從 execution 借用內(nèi)存是如何在分配 storage 內(nèi)存中發(fā)揮作用的

這一過程對應(yīng)的實現(xiàn)是 UnifiedMemoryManager#acquireStorageMemory鬼佣,上面的流程圖應(yīng)該說明了是如何 storage 內(nèi)存及在 storage 內(nèi)存不足時是如何向 execution 借用內(nèi)存的

acquireExecutionMemory

該方法是給 execution 給指定 task 分配內(nèi)存的實現(xiàn)驶拱,當 execution pool 內(nèi)存不足時,會從 storage pool 中借晶衷。該方法在某些情況下可能會阻塞直到有足夠空閑內(nèi)存蓝纲。

在該方法內(nèi)部定義了兩個函數(shù):

  • maybeGrowExecutionPool:會釋放storage中保存的數(shù)據(jù),減小storage部分內(nèi)存大小晌纫,從而增大Execution部分
  • computeMaxExecutionPoolSize:計算在 storage 釋放內(nèi)存借給 execution 后税迷,execution 部分的內(nèi)存大小

在定義了這兩個方法后,直接調(diào)用 ExecutionMemoryPool#acquireMemory 方法锹漱,acquireMemory方法會一直處理該 task 的請求翁狐,直到分配到足夠內(nèi)存或系統(tǒng)判斷無法滿足該請求為止。acquireMemory 方法內(nèi)部有一個死循環(huán)凌蔬,循環(huán)內(nèi)部邏輯如下:

從上面的流程圖中露懒,我們可以知道當 execution pool 要為某個 task 分配內(nèi)存并且內(nèi)存不足時,會從 storage pool 中借用內(nèi)存砂心,能借用的最大 size 為 storage 的空閑內(nèi)存+之前 storage 從 execution 借走的內(nèi)存懈词。這與 storage 從 execution 借用內(nèi)存不同,storage 只能從 execution 借走空閑的內(nèi)存辩诞,不能借走 execution 中已在使用的從 storage 借來的內(nèi)存坎弯,源碼中的解釋是如果要這么做實現(xiàn)太過復(fù)雜,暫時不支持译暂。

以上過程分析的是memoryMode 為 ON_HEAP 的情況抠忘,如果是 OFF_HEAP,則直接從 offHeapExecution 內(nèi)存池中分配外永,本文重點不在此崎脉,故不展開分析。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伯顶,一起剝皮案震驚了整個濱河市囚灼,隨后出現(xiàn)的幾起案子骆膝,更是在濱河造成了極大的恐慌,老刑警劉巖灶体,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阅签,死亡現(xiàn)場離奇詭異,居然都是意外死亡蝎抽,警方通過查閱死者的電腦和手機政钟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來樟结,“玉大人养交,你說我怎么就攤上這事∠梁穑” “怎么了层坠?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長刁笙。 經(jīng)常有香客問我破花,道長,這世上最難降的妖魔是什么疲吸? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任座每,我火速辦了婚禮,結(jié)果婚禮上摘悴,老公的妹妹穿的比我還像新娘峭梳。我一直安慰自己,他們只是感情好蹂喻,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布葱椭。 她就那樣靜靜地躺著,像睡著了一般口四。 火紅的嫁衣襯著肌膚如雪孵运。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天蔓彩,我揣著相機與錄音治笨,去河邊找鬼。 笑死赤嚼,一個胖子當著我的面吹牛旷赖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播更卒,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼等孵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逞壁?” 一聲冷哼從身側(cè)響起流济,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤锐锣,失蹤者是張志新(化名)和其女友劉穎腌闯,沒想到半個月后绳瘟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡姿骏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年糖声,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片分瘦。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡蘸泻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嘲玫,到底是詐尸還是另有隱情悦施,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布去团,位于F島的核電站抡诞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏土陪。R本人自食惡果不足惜昼汗,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鬼雀。 院中可真熱鬧顷窒,春花似錦、人聲如沸源哩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽励烦。三九已至谓着,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崩侠,已是汗流浹背漆魔。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留却音,地道東北人改抡。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像系瓢,于是被迫代替她去往敵國和親阿纤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容