Spark(二): 內存管理

     Spark 做爲一個以擅長內存計算爲優點的計算引擎,內存管理方案是其很是重要的模塊; Spark的內存能夠大致歸爲兩類:execution和storage,前者包括shuffles、joins、sorts和aggregations所需內存,後者包括cache和節點間數據傳輸所需內存;在Spark 1.5和以前版本里,二者是靜態配置的,不支持借用,spark1.6 對內存管理模塊進行了優化,經過內存空間的融合,消除以上限制,提供更好的性能。官方網站只是要求內存在8GB之上便可(Impala推薦要求機器配置在128GB), 但spark job運行效率主要取決於:數據量大小,內存消耗,內核數(肯定併發運行的task數量)git

目錄:github

  •  基礎知識
  • spark1.5- 內存管理
  • spark1.6 內存管理

基本知識:算法


  • on-heap memory:Java中分配的非空對象都是由Java虛擬機的垃圾收集器管理的,也稱爲堆內內存。虛擬機會按期對垃圾內存進行回收,在某些特定的時間點,它會進行一次完全的回收(full gc)。完全回收時,垃圾收集器會對全部分配的堆內內存進行完整的掃描,這意味着一個重要的事實——這樣一次垃圾收集對Java應用形成的影響,跟堆的大小是成正比的。過大的堆會影響Java應用的性能
  • off-heap memory:堆外內存意味着把內存對象分配在Java虛擬機的堆之外的內存,這些內存直接受操做系統管理(而不是虛擬機)。這樣作的結果就是能保持一個較小的堆,以減小垃圾收集對應用的影響
  • LRU Cache(Least Recently Used):LRU能夠說是一種算法,也能夠算是一種原則,用來判斷如何從Cache中清除對象,而LRU就是「近期最少使用」原則,當Cache溢出時,最近最少使用的對象將被從Cache中清除
  • spark 源碼: https://github.com/apache/spark/releases
  • scale ide for Intellij : http://plugins.jetbrains.com/plugin/?id=1347

Spark1.5- 內存管理:apache


  • 1.6 版本引入了新的內存管理方案,配置參數: spark.memory.useLegacyMode 默認 false 表示使用新方案,true 表示使用舊方案, SparkEnv.scala 源碼 以下圖:
  •  
  • 在staticMemoryManager.scala 類中查看構造類及內存獲取定義
  •       

  • 經過代碼推斷,若設置了 spark.testing.memory 則以該配置的值做爲 systemMaxMemory,不然使用 JVM 最大內存做爲 systemMaxMemory。
  • spark.testing.memory 僅用於測試,通常不設置,因此這裏咱們認爲 systemMaxMemory 的值就是 executor 的最大可用內存
  • Execution:用於緩存shuffle、join、sort和aggregation的臨時數據,經過spark.shuffle.memoryFraction配置
  • spark.shuffle.memoryFraction:shuffle 期間佔 executor 運行時內存的百分比,用小數表示。在任什麼時候候,用於 shuffle 的內存總 size 不得超過這個限制,超出部分會 spill 到磁盤。若是常常 spill,考慮調大參數值
  • spark.shuffle.safetyFraction:爲防止 OOM,不能把 systemMaxMemory * spark.shuffle.memoryFraction 全用了,須要有個安全百分比
  • 最終用於 execution 的內存量爲:executor 最大可用內存* spark.shuffle.memoryFraction*spark.shuffle.safetyFraction,默認爲 executor 最大可用內存 * 0.16
  • execution內存被分配給JVM裏的多個task線程。
  • task間的execution內存分配是動態的,若是沒有其餘tasks存在,Spark容許一個task佔用全部可用execution內存

  • storage內存分配分析過程與 Execution 一致,由上面的代碼得出,用於storage 的內存量爲: executor 最大可用內存 * spark.storage.memoryFraction * spark.storage.safetyFraction,默認爲 executor 最大可用內存 * 0.54
  • 在 storage 中,有一部份內存是給 unroll 使用的,unroll 即反序列化 block,該部分佔比由 spark.storage.unrollFraction 控制,默認爲0.2

  • 經過代碼分析,storage 和 execution 總共使用了 80% 的內存,剩餘 20% 內存被系統保留了,用來存儲運行中產生的對象,該類型內存不可控.

小結:緩存


  • 這種內存管理方式的缺陷,即 execution 和 storage 內存表態分配,即便在一方內存不夠用而另外一方內存空閒的狀況下也不能共享,形成內存浪費,爲解決這一問題,spark1.6 啓用新的內存管理方案UnifiedMemoryManager
  • staticMemoryManager- jvm 堆內存分配圖以下

 

Spark1.6 內存管理:安全


  • 從spark1.6開始,引入了新的內存管理方式-----統一內存管理(UnifiedMemoryManager),在統一內存管理下,spark一個executor中的jvm heap內存被劃分紅以下圖:併發

  • Reserved Memory,這一部分的內存是咱們沒法使用的部分,spark內部保留內存,會存儲一些spark的內部對象等內容。
  • spark1.6默認的Reserved Memory大小是300MB。這部分大小是不容許咱們使用者改變的。簡單點說就是咱們在爲executor申請內存後,有300MB是咱們沒法使用的。而且若是咱們申請的executor的大小小於1.5 * Reserved Memory 即 < 450MB,spark會報錯:
  • User Memory:用戶在程序中建立的對象存儲等一系列非spark管理的內存開銷都佔用這一部份內存
  • Spark Memory:該部分大小爲 (JVM Heap Size - Reserved Memory) * spark.memory.fraction,其中的spark.memory.fraction能夠是咱們配置的(默認0.75),以下圖:
  • 若是spark.memory.fraction配小了,咱們的spark task在執行時產生數據時,包括咱們在作cache時就極可能出現常常由於這部份內存不足的狀況而產生spill到disk的狀況,影響效率。採用官方推薦默認配置
  • Spark Memory這一塊有被分紅了兩個部分,Execution Memory 和 Storage Memory,這經過spark.memory.storageFraction來配置兩塊各佔的大小(默認0.5,一邊一半),如圖:
  • Storage Memory主要用來存儲咱們cache的數據和臨時空間序列化時unroll的數據,以及broadcast變量cache級別存儲的內容
  • Execution Memory則是spark Task執行時使用的內存(好比shuffle時排序就須要大量的內存)
  • 爲了提升內存利用率,spark針對Storage Memory 和 Execution Memory有以下策略:
    1. 一方空閒,一方內存不足狀況下,內存不足一方能夠向空閒一方借用內存
    2. 只有Execution Memory能夠強制拿回Storage Memory在Execution Memory空閒時,借用的Execution Memory的部份內存(若是因強制取回,而Storage Memory數據丟失,從新計算便可)
    3. 若是Storage Memory只能等待Execution Memory主動釋放佔用的Storage Memory空閒時的內存。(這裏不強制取回,由於若是task執行,數據丟失就會致使task 失敗)
相關文章
相關標籤/搜索