總結(jié)一下spark中內(nèi)存分配,希望能給在座各位在工作中或面試中一點(diǎn)幫助!轉(zhuǎn)載請注明出處:Michael孟良
在Spark 1.5版本及以前,Spark采用靜態(tài)內(nèi)存管理模型仑扑。Spark 1.6版本推出以后,Spark采用了統(tǒng)一內(nèi)存管理模型置鼻。
靜態(tài)內(nèi)存管理模型中
Spark在一個Executor中的內(nèi)存分為三塊镇饮,一塊是execution內(nèi)存,一塊是storage內(nèi)存箕母,一塊是other內(nèi)存储藐。
JVM On_heap 內(nèi)存
1.storage內(nèi)存是存儲broadcast,cache嘶是,persist數(shù)據(jù)的地方钙勃。其中10%(60%10%)用于防止OOM。另外90%中的20%用于unroll聂喇,數(shù)據(jù)展開的(比如說辖源,rdd.perist讓數(shù)據(jù)序列化持久化,當(dāng)要讀出來的時候就需要反序列化希太,可以理解為解壓克饶,這就需要unroll這部分的內(nèi)存空間了),其余的內(nèi)存(90%80%)用于RDD緩存數(shù)據(jù)和廣播變量誊辉。
2.execution內(nèi)存是執(zhí)行內(nèi)存矾湃,文檔中說join,aggregate都在這部分內(nèi)存中執(zhí)行堕澄,shuffle的數(shù)據(jù)也會先緩存在這.個內(nèi)存中邀跃,滿了再寫入磁盤,能夠減少IO蛙紫。其實(shí)map過程也是在這個內(nèi)存中執(zhí)行的拍屑。
3.other內(nèi)存是程序執(zhí)行時預(yù)留給自己的內(nèi)存,像task的執(zhí)行和task執(zhí)行時產(chǎn)生的對象惊来。
從Spark 1.6版本推出以后丽涩,Spark采用了統(tǒng)一內(nèi)存管理模型棺滞。Spark 2.1.0 新型 JVM Heap 分成三個部份:Reserved Memory裁蚁、User Memory 和 Spark Memor。
Spark Memeory:
系統(tǒng)框架運(yùn)行時需要使用的空間继准,這是從兩部份構(gòu)成的枉证,分別是 Storage Memeory 和 Execution Memory。現(xiàn)在 Storage 和 Execution (Shuffle) 采用了 Unified 的方式共同使用了:
(Java Heap – ReservedMemory) * spark.memory.fraction
在Spark 1.6.1中移必,默認(rèn)為(Java Heap - 300M) * 0.75
在Spark 2.2.0中室谚,默認(rèn)為(Java Heap - 300M) * 0.6
默認(rèn)情況下 Storage 和 Execution 各占該空間的 50%。你可以從圖中可以看出,Storgae 和 Execution 的存儲空間可以往上和往下移動秒赤。
所謂 Unified 的意思是 Storgae 和 Execution 在適當(dāng)時候可以借用彼此的 Memory猪瞬,需要注意的是,當(dāng) Execution 空間不足而且 Storage 空間也不足的情況下入篮,Storage 空間如果曾經(jīng)使用了超過 Unified 默認(rèn)的 50% 空間的話則超過部份會被強(qiáng)制 drop 掉一部份數(shù)據(jù)來解決 Execution 空間不足的問題 (注意:drop 后數(shù)據(jù)會不會丟失主要是看你在程序設(shè)置的 storage_level 來決定你是 Drop 到那里陈瘦,可能 Drop 到磁盤上),這是因?yàn)閳?zhí)行(Execution) 比緩存 (Storage) 是更重要的事情潮售。
User Memory:
寫 Spark 程序中產(chǎn)生的臨時數(shù)據(jù)或者是自己維護(hù)的一些數(shù)據(jù)結(jié)構(gòu)也需要給予它一部份的存儲空間痊项,你可以這么認(rèn)為,這是程序運(yùn)行時用戶可以主導(dǎo)的空間酥诽,叫用戶操作空間鞍泉。它占用的空間是 :
(Java Heap - Reserved Memory) x 25% (默認(rèn)是25%,可以有參數(shù)供調(diào)優(yōu))
在Spark 1.6.1中肮帐,默認(rèn)占(Java Heap - 300MB) * 0.25
在Spark 2.2.0中咖驮,默認(rèn)占(Java Heap - 300MB) * 0.4
這樣設(shè)計可以讓用戶操作時所需要的空間與系統(tǒng)框架運(yùn)行時所需要的空間分離開。假設(shè) Executor 有 4G 的大小训枢,那么在默認(rèn)情況下 User Memory 大小是:(4G - 300MB) x 25% = 949MB游沿,也就是說一個 Stage 內(nèi)部展開后 Task 的算子在運(yùn)行時最大的大小不能夠超過 949MB。例如工程師使用 mapPartition 等肮砾,一個 Task 內(nèi)部所有所有算子使用的數(shù)據(jù)空間的大小如果大于 949MB 的話诀黍,那么就會出現(xiàn) OOM。
思考題:有 100個 Executors 每個 4G 大小仗处,現(xiàn)在要處理 100G 的數(shù)據(jù)眯勾,假設(shè)這 100G 分配給 100個 Executors,每個 Executor 分配 1G 的數(shù)據(jù)婆誓,這 1G 的數(shù)據(jù)遠(yuǎn)遠(yuǎn)少于 4G Executor 內(nèi)存的大小吃环,為什么還會出現(xiàn) OOM 的情況呢?那是因?yàn)樵谀愕拇a中(e.g.你寫的應(yīng)用程序算子)超過用戶空間的限制 (e.g. 949MB)洋幻,而不是 RDD 本身的數(shù)據(jù)超過了限制郁轻。
Reserved Memory:
默認(rèn)都是300MB,這個數(shù)字一般都是固定不變的文留,在系統(tǒng)運(yùn)行的時候 Java Heap 的大小至少為 Heap Reserved Memory x 1.5. e.g. 300MB x 1.5 = 450MB 的 JVM配置好唯。一般本地開發(fā)例如說在 Windows 系統(tǒng)上,建義系統(tǒng)至少 2G 的大小燥翅。
ExecutionMemory 和 StorageMemory 會共享usableMemory * spark.memory.fraction(默認(rèn)0.75)
注意:
在Spark 1.6.1 中spark.memory.fraction默認(rèn)為0.75
在Spark 2.2.0 中spark.memory.fraction默認(rèn)為0.6
如果是你的計算比較復(fù)雜的情況骑篙,使用新型的內(nèi)存管理 (Unified Memory Management) 會取得更好的效率,但是如果說計算的業(yè)務(wù)邏輯需要更大的緩存空間森书,此時使用老版本的固定內(nèi)存管理 (StaticMemoryManagement) 效果會更好