OpenJDK 8 有多種 GC(Garbage Collector)算法,如 Parallel GC、CMS 和 G1。哪一個才是最快的呢?如果在 Java 9 中將 Java 8 默認的 GC 從 Parallel GC 改為 G1 (目前只是建議)將會怎麼樣呢?讓我們對此進行基准測試。
運行相同的代碼六次,每次使用不同的VM參數(-XX:+UseSerialGC, -XX:+UseParallelGC, -XX:+UseConcMarkSweepGC, -XX:ParallelCMSThreads=2, -XX:ParallelCMSThreads=4, -XX:+UseG1GC)。
每次運行大概花費55分鐘。
其它VM參數:-Xmx2048M -server
OpenJDK版本:1.8.0_51(當前最新的版本)
軟件:Linux version 4.0.4-301.fc22.x86_64
硬件:Intel? Core? i7-4790 CPU @ 3.60GHz
每次運行13個?OptaPlanner?規劃問題方案。每次運行時間為5分鐘。前30秒用於JVM預熱,不計算在內。
解決規劃問題不涉及 IO (除了啟動時需要幾毫秒來加載輸入信息)。單個 CPU 使用完全飽和。通常會創建許多存活時間很短的對象,GC 之後就會回收這些對象。
衡量標准可以是計算每毫秒的得分,越高越好。計算一個擬議規劃解決方案是一個不可小觑的問題:涉及到大量的計算,包括每個實體與其他所有實體的沖突檢測。
為了能在本地重復運行這些基准測試,可以從源碼進行構建,然後運行主類 GeneralOptaPlannerBenchmarkApp。
執行結果
為了方便查看,我已經對每種 GC 與 Java 8 默認 GC(Parallel GC)進行了比較。
結果非常清楚:默認(Parallel GC)是最快的。
原始基准測試數據
相對基准測試數據
有一種提議是在 OpenJDK9 的服務器端使用 G1 作為默認 GC。我第一反應就是拒絕該提議:
G1 的平均值要慢17.60%
G1 在每個數據集用例下都比較慢。
在最大數據集(Machine Reassignment B10)下,表現比其它數據集都要差,G1 慢了34.07%。
如果在開發機和服務器之間采用不同的默認 GC,則開發者基准測試的可信度就會下降。
另一方面,存在幾個需要注意的細節:
G1 關注是 GC 暫停的問題,而不是吞吐量。對於這些用例(計算量比較大),GC 暫停時長基本沒影響。
這是一個(基本是)單線程的基准測試。並行解決多個問題或采用多線程解決的基准測試,結果可能不同。
G1 推薦的堆內存至少是 6GB。而這次基准測試的堆內存是 2GB,即使在最大數據集(Machine Reassignment B10)也只需要這麼多內存。
海量計算只是 OpenJDK 的諸多功能中的一個:這是在社區廣泛爭論的一個問題。如果有其他方面(如網站服務)的證明,可能值得改變默認GC。但是,請首先向我展示你實際項目的基准測試。
在 Java 8 中,對 OptaPlanner 用例來說,默認 GC(Parallel GC)通常情況是最好的選擇。