大數(shù)據(jù)計(jì)算框架Spark之內(nèi)存模型
Executor 端的內(nèi)存模型,包括堆內(nèi)內(nèi)存(On-heap Memory)和堆外內(nèi)存(Off-heap Memory)。

存管理接口(MemoryManager )
Spark 為Execution 內(nèi)存和Storage 內(nèi)存的管理提供了統(tǒng)一的接:MemoryManager。
MemoryManager 的具體實(shí)現(xiàn)上,Spark 1.6 之后默認(rèn)為統(tǒng)一管理(Unified Memory Manager)方式,1.6 之前采用的靜態(tài)管理(Static Memory Manager)方式仍被保留,可通過(guò)配置 spark.memory.useLegacyMode 參數(shù)啟用。
1靜態(tài)內(nèi)存管理(Static Memory Manager)
堆內(nèi)空間管理
存儲(chǔ)內(nèi)存、執(zhí)行內(nèi)存和其他內(nèi)存的大小在 Spark 應(yīng)用程序運(yùn)行期間均為固定的,但可以在應(yīng)用程序啟動(dòng)前進(jìn)行配置。
可用的Execution 內(nèi)存和 可用的Storage 內(nèi)存計(jì)算公式:
可用的Execution 內(nèi)存 = systemMaxMemory * spark.shuffle.memoryFraction * spark.shuffle.safetyFraction
可用的Storage 內(nèi)存 = systemMaxMemory * spark.storage.memoryFraction * spark.storage.safetyFraction
systemMaxMemory 取決于當(dāng)前 JVM 堆內(nèi)內(nèi)存的大小,公式中的兩個(gè) safetyFraction 參數(shù),其意義在于在邏輯上預(yù)留出一塊保險(xiǎn)區(qū)域,降低因?qū)嶋H內(nèi)存超出當(dāng)前預(yù)設(shè)范圍而導(dǎo)致 OOM 的風(fēng)險(xiǎn)。這塊邏輯上預(yù)留的區(qū)域同樣由JVM管理。

圖靜態(tài)內(nèi)存管理-堆內(nèi)內(nèi)存的分配
堆外空間管理
堆外只有Execution 內(nèi)存和Storage 內(nèi)存,他們的大小由spark.memory.storageFraction這個(gè)參數(shù)決定。由于堆外內(nèi)存占用的空間可以被精確計(jì)算,所以無(wú)需再設(shè)定保險(xiǎn)區(qū)域

靜態(tài)內(nèi)存管理-堆外內(nèi)存的分配
2統(tǒng)一內(nèi)存管理
Spark 1.6 之后引入的統(tǒng)一內(nèi)存管理機(jī)制,與靜態(tài)內(nèi)存管理的區(qū)別在于Storage 內(nèi)存和Execution 內(nèi)存共享同一塊空間,可以動(dòng)態(tài)占用對(duì)方的空閑區(qū)域。
堆內(nèi)內(nèi)存(On-heap Memory)
堆內(nèi)內(nèi)存區(qū)域大致可以分為以下四類:
1)Execution 內(nèi)存:主要用于存放 Shuffle、Join、Sort、Aggregation 等計(jì)算過(guò)程中的臨時(shí)數(shù)據(jù)
2)Storage 內(nèi)存:主要用于緩存和傳播內(nèi)部數(shù)據(jù)
3)Other:用戶數(shù)據(jù)結(jié)構(gòu)、Spark內(nèi)部元數(shù)據(jù)。
4)預(yù)留內(nèi)存(Reserved Memory):系統(tǒng)預(yù)留內(nèi)存。默認(rèn)是300MB。

堆內(nèi)存(Off-heap Memory)
Spark 1.6 開(kāi)始引入了Off-heap memory(詳見(jiàn)SPARK-11389)。這種模式不在 JVM 內(nèi)申請(qǐng)內(nèi)存,而是調(diào)用 Java unsafe API (如 C 語(yǔ)言里面的 malloc())直接向操作系統(tǒng)申請(qǐng)內(nèi)存,JVM不管理這部分內(nèi)存,所以可以避免頻繁的 GC,但要自己編寫內(nèi)存申請(qǐng)和釋放的邏輯。
堆外內(nèi)存分為兩類:Execution 內(nèi)存和 Storage 內(nèi)存。
默認(rèn)情況下堆外內(nèi)存不啟用,可以通過(guò)配置spark.memory.offHeap.enabled 參數(shù)啟用,通過(guò)配置spark.memory.offHeap.size 參數(shù)設(shè)定堆外內(nèi)存空間的大小。
如果堆外內(nèi)存被啟用,那么 Executor 內(nèi)將同時(shí)存在堆內(nèi)和堆外內(nèi)存,Executor 中的 Execution 內(nèi)存是堆內(nèi)的 Execution 內(nèi)存和堆外的 Execution 內(nèi)存之和,同理,Storage 內(nèi)存也一樣。

說(shuō)明
maxOffHeapMemory=spark.memory.offHeap.size
Execution 內(nèi)存和 Storage 內(nèi)存動(dòng)態(tài)調(diào)整
1)設(shè)定基本的Storage 內(nèi)存和Execution 內(nèi)存區(qū)域(通過(guò)spark.storage.storageFraction配置比例,默認(rèn)是0.5,即各占一半)
2)Storage 內(nèi)存和Execution 內(nèi)存都不足時(shí),則存儲(chǔ)到硬盤;若己方空間不足而對(duì)方空余時(shí),可借用對(duì)方的空間;(存儲(chǔ)空間不足是指不足以放下一個(gè)完整的 Block)
3)Execution 內(nèi)存空間被對(duì)方占用后,可讓對(duì)方將占用的部分轉(zhuǎn)存到硬盤,然后"歸還"借用的空間
4)Storage 內(nèi)存空間被對(duì)方占用后,無(wú)法讓對(duì)方"歸還",因?yàn)閷?shí)現(xiàn)起來(lái)較為復(fù)雜。

動(dòng)態(tài)整