文章地址:http://www.haha174.top/article/details/259787
背景
如果在持久化RDD的時候持久化了大量的數(shù)據(jù)那么java 虛擬機(jī)在垃圾回收的時候就可能成為一個性能瓶頸。因為java虛擬機(jī)會定期的進(jìn)行垃圾回收,此時會最總所有的java對象并且在垃圾回收時找到些不在使用的對象進(jìn)行回收篙挽。
垃圾回收的性能開銷摇零,是根內(nèi)存中對象的數(shù)量成正比的所以對于垃圾回收的性能問題首先要做的是,使用高效的數(shù)據(jù)結(jié)構(gòu),比如array和String 其次在持久化RDD時候。使用序列化持久化級別而且用kyro 這樣的序列化類庫,這樣每個partition就只是一個對象--一個字節(jié)數(shù)組
gc對性能的影響就在于如果內(nèi)存中數(shù)據(jù)比較大的話哮塞,那么可能會很頻繁就會在成內(nèi)存空間滿了不夠用了此時gc就會很頻繁的發(fā)生那么本身gc 就是有性能的消耗。而且還頻繁發(fā)生凳谦,那么對性能當(dāng)然有影響啦忆畅。
此外如果數(shù)據(jù)量過大的話,那么每次gc 的時候要回收的是不是也特別多尸执。那么會導(dǎo)致gc 的速度比較慢家凯。除此之外gc 發(fā)生的時候,gc 是一個線程那么比如說task 是工作線程gc 運(yùn)行的時候會讓工作線程停下來如失。讓gc單獨(dú)運(yùn)行這樣就會直接導(dǎo)致了我們task執(zhí)行的停止绊诲,印象spark線程的執(zhí)行速度,降低spark的性能褪贵。
監(jiān)測:
我們可以對垃圾回收進(jìn)行監(jiān)測掂之,包括多久進(jìn)行一次回收,以每次回收的耗費(fèi)時間脆丁。只要在spark-submit腳本中添加一個配置即可世舰。
--conf "spark.executor.extra.javaOptions=-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamaps"
但是要記住這里雖然會打印java 虛擬機(jī)的垃圾回收的相關(guān)信息但是輸出到了worker的日志上額不是driver 的日志上。
但是這種方式也是一種槽卫,其實完全可以通過SparkUI來觀察每個stage的垃圾回收的情況
優(yōu)化executor內(nèi)存比例
對于垃圾回收來說跟压,最重要的就是調(diào)節(jié)RDD緩存中占用的內(nèi)存空間,與算子執(zhí)行時創(chuàng)建的對象占用的內(nèi)存空間的比例歼培,默認(rèn)情況下震蒋,spark使用每個executor 60%的內(nèi)存空間來緩存RDD茸塞。那么在task執(zhí)行期間創(chuàng)建的對象只有40%的空間來存存放。
在這種情況下查剖,很有可能因為你的內(nèi)存空間不足钾虐,task創(chuàng)建的對象過大,那么一旦發(fā)現(xiàn)40%的內(nèi)存空間不夠用了笋庄,就會觸發(fā)java虛擬機(jī)的垃圾回收操作禾唁。因此在極端的情況下垃圾回收可能會頻繁的觸發(fā)。
在上述情況下 无切,如果發(fā)現(xiàn)垃圾回收頻繁的發(fā)生沒那么就需要對這個比例進(jìn)行優(yōu)化。使用
conf.set("spark.storage.memoryFunction","0.5")即可丐枉,
可以將RDD緩存占用空間的比例降低從而給更多的task常見的對象進(jìn)行使用
因此對于RDD的持久化完全可以使用kyro序列化哆键,加上降低其executor內(nèi)存占比的方式,來減少其內(nèi)存消耗瘦锹,給task提供更多的內(nèi)存籍嘹,從而避免task的執(zhí)行頻繁的垃圾回收。
垃圾回收調(diào)優(yōu)1
java堆空間被劃分成了兩塊空間弯院,一個是年輕代辱士,一個是老年代。年輕代放的是短時間的存活的對象听绳,老年代放的是長時間的存活對象颂碘。年輕代又被劃分成了三塊空間,Eden,Survivor1,Survivor2.
首先Eden區(qū)域和Survivor1區(qū)域用于存放對象椅挣,Survivor2區(qū)域備用头岔。創(chuàng)建的對象,首先放入Eden區(qū)域和Survivor1區(qū)域鼠证,如果Eden區(qū)域滿了峡竣,那么就會觸發(fā)一次Minor GC,進(jìn)行年輕代的垃圾回收量九。Eden和Survivor1區(qū)域中存活的對象适掰,會被移動到Survivor2區(qū)域中。然后Survivor1和Survivor2的角色調(diào)換荠列。Survivor1變成了備用类浪。
如果一個對象,在年輕代肌似,撐過了多次垃圾回收棺克,都沒有被回收掉,那么會被認(rèn)為是長時間存活的伙菜,此時就會被移入老年代。此外垦搬,如果在將Eden和Survivor1中存活對象,嘗試放入Survivor2中時艳汽,發(fā)現(xiàn)Survivor2放滿了猴贰,那么會直接放入老年代。此時就出現(xiàn)了河狐,超時間存活的對象米绕,進(jìn)入老年代的問題。
如果老年代空間滿了沒那么就會觸發(fā)full GC進(jìn)行老年的垃圾回收操作馋艺。
垃圾回收調(diào)優(yōu)2
Spark中垃圾回收調(diào)優(yōu)的目標(biāo)就是栅干,只有真正長時間存活的對象,才能進(jìn)入老年代捐祠,短時間存活的對象碱鳞,對只能呆在年輕代。不能因為某個Survivor區(qū)域空間不夠踱蛀,在Mintor GC時窿给,就進(jìn)入了老年代。從而造成了短時間存活的對象率拒,長期呆在老年代中占據(jù)了空間崩泡,而且full GC時要回收大量的短時間存活的對象,導(dǎo)致full GC速度緩慢猬膨。
如果發(fā)現(xiàn)角撞,在task執(zhí)行期間,大量full gc 發(fā)生了 勃痴,那么說明靴寂,年輕代的Survivor區(qū)域,給的空間不夠大召耘,此時可以執(zhí)行一些操作來優(yōu)化垃圾回收行為:
1.包括降低spark.storage.memoryFraction的比例百炬,給年輕代更多的空間,來存放短時間存活的對象污它;
2.給Eden 區(qū)域分配更大的空間剖踊,使用-Xmm即可 ,通常建議給Eden 區(qū)域衫贬,預(yù)計大小的4/3;
3.如果使用的是HDFS文件德澈,那么很好估計Eden區(qū)域大小,如果executor有4個task.然后每個hdfs壓縮塊 解壓縮后大小是3倍固惯,此外每個hdfs塊的大小是64m梆造,那么Eden區(qū)域的預(yù)計大小就是:4364MB.