Spark效能優化:JVM引數調優
關於JVM垃圾回收種類
Minor GC
從年輕代空間(包括 Eden 和 Survivor 區域)回收記憶體被稱為 Minor GC。這一定義既清晰又易於理解。但是,當發生Minor GC事件的時候,有一些有趣的地方需要注意到:
當 JVM 無法為一個新的物件分配空間時會觸發 Minor GC,比如當 Eden 區滿了。所以分配率越高,越頻繁執行 Minor GC。
記憶體池被填滿的時候,其中的內容全部會被複制,指標會從0開始跟蹤空閒記憶體。Eden 和 Survivor 區進行了標記和複製操作,取代了經典的標記、掃描、壓縮、清理操作。所以 Eden 和 Survivor 區不存在記憶體碎片。寫指標總是停留在所使用記憶體池的頂部。
執行 Minor GC 操作時,不會影響到永久代。從永久代到年輕代的引用被當成 GC roots,從年輕代到永久代的引用在標記階段被直接忽略掉。
質疑常規的認知,所有的 Minor GC 都會觸發“全世界的暫停(stop-the-world)”,停止應用程式的執行緒。對於大部分應用程式,停頓導致的延遲都是可以忽略不計的。其中的真相就 是,大部分 Eden 區中的物件都能被認為是垃圾,永遠也不會被複制到 Survivor 區或者老年代空間。如果正好相反,Eden 區大部分新生物件不符合 GC 條件,Minor GC 執行時暫停的時間將會長很多。
所以 Minor GC 的情況就相當清楚了——每次 Minor GC 會清理年輕代的記憶體。
Major GC vs Full GC
大家應該注意到,目前,這些術語無論是在 JVM 規範還是在垃圾收集研究論文中都沒有正式的定義。但是我們一看就知道這些在我們已經知道的基礎之上做出的定義是正確的,Minor GC 清理年輕帶記憶體應該被設計得簡單:
Major GC 是清理永久代。
Full GC 是清理整個堆空間—包括年輕代和永久代。
很不幸,實際上它還有點複雜且令人困惑。首先,許多 Major GC 是由 Minor GC 觸發的,所以很多情況下將這兩種 GC 分離是不太可能的。另一方面,許多現代垃圾收集機制會清理部分永久代空間,所以使用“cleaning”一詞只是部分正確。
這使得我們不用去關心到底是叫 Major GC 還是 Full GC,大家應該關注當前的 GC 是否停止了所有應用程式的執行緒,還是能夠併發的處理而不用停掉應用程式的執行緒。
這種混亂甚至內建到 JVM 標準工具。下面一個例子很好的解釋了我的意思。讓我們比較兩個不同的工具 Concurrent Mark 和 Sweep collector (-XX:+UseConcMarkSweepGC)在 JVM 中執行時輸出的跟蹤記錄。
依據物件的存活週期進行分類,短命物件歸為新生代,長命物件歸為老年代。
根據不同代的特點,選取合適的收集演算法
新生代:少量物件存活,適合複製演算法
老年代:大量物件存活,適合標記清理或者標記壓縮
Young:主要是用來存放新生的物件。
Old:主要存放應用程式中生命週期長的記憶體物件。
Permanent:是指記憶體的永久儲存區域,主要存放Class和Meta的資訊,Class在被 Load的時候被放入PermGen space區域. 它和和存放Instance的Heap區域不同,GC(Garbage Collection)不會在主程式執行期對PermGen space進行清理,所以如果你的APP會LOAD很多CLASS的話,就很可能出現PermGen space錯誤
上圖演示新生代GC過程用到的複製演算法,黃色表示死物件,綠色表示剩餘空間,紅色表示倖存物件
上圖演示老年代GC時用到的標記-壓縮演算法
總結一下,物件一般出生在Eden區,年輕代GC過程中,物件在2個倖存區之間移動,如果物件存活到適當的年齡,會被移動到老年代。當物件在老年代死亡時,就需要更高級別的GC,更重量級的GC演算法(複製演算法不適用於老年代,因為沒有多餘的空間用於複製)
Shell提交指令碼例項:
#!/bin/bash
source /etc/profile
nohup /opt/modules/spark/bin/spark-submit \
--master spark://10.130.2.20:7077 \
--conf "spark.executor.extraJavaOptions=-XX:PermSize=8m -XX:SurvivorRatio=4 -XX:NewRatio=4 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps" \
--driver-memory 1g \
--executor-memory 1g \
--total-executor-cores 48 \
--conf "spark.ui.port=8088" \
--jars /opt/bin/sparkJars/kafka_2.10-0.8.2.1.jar,/opt/bin/sparkJars/spark-streaming-kafka_2.10-1.4.1.jar,/opt/bin/sparkJars/metrics-core-2.2.0.jar,/opt/bin/sparkJars
/mysql-connector-java-5.1.26-bin.jar,/opt/bin/sparkJars/spark-streaming-kafka_2.10-1.4.1.jar \
--class com.spark.streaming.Top3HotProduct \
SparkApp.jar \
> sparkApp.log 2>&1 & \