1. 程式人生 > >spark調優----垃圾回收

spark調優----垃圾回收

背景:
    如果在持久化RDD的時候持久化了大量的資料那麼java 虛擬機器在垃圾回收的時候就可能成為一個性能瓶頸。因為java虛擬機器會定期的進行垃圾回收,此時會最總所有的java物件並且在垃圾回收時找到些不在使用的物件進行回收。
垃圾回收的效能開銷,是根記憶體中物件的數量成正比的所以對於垃圾回收的效能問題首先要做的是,使用高效的資料結構,比如array和String 其次在持久化RDD時候。使用序列化持久化級別而且用kyro  這樣的序列化類庫,這樣每個partition就只是一個物件–一個位元組陣列
gc對效能的影響就在於如果記憶體中資料比較大的話,那麼可能會很頻繁就會在成記憶體空間滿了不夠用了此時gc就會很頻繁的發生那麼本身gc  就是有效能的消耗。而且還頻繁發生,那麼對效能當然有影響啦。
此外如果資料量過大的話,那麼每次gc  的時候要回收的是不是也特別多。那麼會導致gc  的速度比較慢。除此之外gc  發生的時候,gc  是一個執行緒那麼比如說task  是工作執行緒gc 執行的時候會讓工作執行緒停下來。讓gc單獨執行這樣就會直接導致了我們task執行的停止,印象spark執行緒的執行速度,降低spark的效能。
監測:
    我們可以對垃圾回收進行監測,包括多久進行一次回收,以每次回收的耗費時間。只要在spark-submit指令碼中新增一個配置即可。
    --conf "spark.executor.extra.javaOptions=-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamaps"1
    但是要記住這裡雖然會列印java  虛擬機器的垃圾回收的相關資訊但是輸出到了worker的日誌上額不是driver  的日誌上。
    但是這種方式也是一種,其實完全可以通過SparkUI來觀察每個stage的垃圾回收的情況
優化executor記憶體比例:
     對於垃圾回收來說,最重要的就是調節RDD快取中佔用的記憶體空間,與運算元執行時建立的物件佔用的記憶體空間的比例,預設情況下,spark使用每個executor 60%的記憶體空間來快取RDD。那麼在task執行期間建立的物件只有40%的空間來存存放。
     在這種情況下,很有可能因為你的記憶體空間不足,task建立的物件過大,那麼一旦發現40%的記憶體空間不夠用了,就會觸發java虛擬機器的垃圾回收操作。因此在極端的情況下垃圾回收可能會頻繁的觸發。
     在上述情況下 ,如果發現垃圾回收頻繁的發生沒那麼就需要對這個比例進行優化。使用
     conf.set("spark.storage.memoryFunction","0.5")即可,1
     可以將RDD快取佔用空間的比例降低從而給更多的task常見的物件進行使用
     因此對於RDD的持久化完全可以使用kyro序列化,加上降低其executor記憶體佔比的方式,來減少其記憶體消耗,給task提供更多的記憶體,從而避免task的執行頻繁的垃圾回收。
垃圾回收調優1:
     java堆空間被劃分成了兩塊空間,一個是年輕代,一個是老年代。年輕代放的是短時間的存活的物件,老年代放的是長時間的存活物件。年輕代又被劃分成了三塊空間,Eden,Survivor1,Survivor2.
     首先Eden區域和Survivor1區域用於存放物件,Survivor2區域備用。建立的物件,首先放入Eden區域和Survivor1區域,如果Eden區域滿了,那麼就會觸發一次Minor GC,進行年輕代的垃圾回收。Eden和Survivor1區域中存活的物件,會被移動到Survivor2區域中。然後Survivor1和Survivor2的角色調換。Survivor1變成了備用。
     如果一個物件,在年輕代,撐過了多次垃圾回收,都沒有被回收掉,那麼會被認為是長時間存活的,此時就會被移入老年代。此外,如果在將Eden和Survivor1中存活物件,嘗試放入Survivor2中時,發現Survivor2放滿了,那麼會直接放入老年代。此時就出現了,超時間存活的物件,進入老年代的問題。
     如果老年代空間滿了沒那麼就會觸發full GC進行老年的垃圾回收操作。
垃圾回收調優2
      Spark中垃圾回收調優的目標就是,只有真正長時間存活的物件,才能進入老年代,短時間存活的物件,對只能呆在年輕代。不能因為某個Survivor區域空間不夠,在Mintor GC時,就進入了老年代。從而造成了短時間存活的物件,長期呆在老年代中佔據了空間,而且full GC時要回收大量的短時間存活的物件,導致full GC速度緩慢。
      如果發現,在task執行期間,大量full gc 發生了 ,那麼說明,年輕代的Survivor區域,給的空間不夠大,此時可以執行一些操作來優化垃圾回收行為:
          1.包括降低spark.storage.memoryFraction的比例,給年輕代更多的空間,來存放短時間存活的物件;
          2.給Eden 區域分配更大的空間,使用-Xmm即可 ,通常建議給Eden 區域,預計大小的4/3;
          3.如果使用的是HDFS檔案,那麼很好估計Eden區域大小,如果executor有4個task.然後每個hdfs壓縮塊 解壓縮後大小是3倍,此外每個hdfs塊的大小是64m,那麼Eden區域的預計大小就是:4*3*64MB.