一、JWarmup背景
(一)應用程序預熱
Java的方法要被執行時,首先這個方法所在的類需要被JVM加載,這個過程包括各類文件的驗證、解析、鏈接以及類的初始化。當這個類被加載進來了以後,JVM就可以去執行這個方法。
JVM在剛開始的時候會使用模板解釋器去解釋執行方法,模板解釋器除了一個個去執行方法中的Bytecodes之外,還會額外收集關於方法執行動態運行的信息,例如方法執行的調用次數,調用時一些類型的信息等。這些信息都會提供給JVM的即時編譯器,由它利用這些信息將剛才解釋執行發現的熱點方法編譯成為Native Code。這樣JVM就不用模板解釋器去執行這些方法,而是去執行性能更高的Native Code,從而使應用程序的性能得到大幅提升。
當大多數熱點方法都被編譯成為Native Code以後,應用程序的預熱就完成了。
(二)遇到的問題
Java有非常豐富的應用場景,一個典型的場景就是我們會用Java寫一些 Web服務。在Web服務的部署過程中,會發現預熱給我們帶來非常大的困擾。當我們把一個Web服務部署到線上後,應用啟動完成,此時就會有大量的用戶請求進入。
這個時候由於有大量的熱點方法需要被編譯,JVM的編譯線程會非常忙碌,因為它需要佔用大量的CPU將這些方法編譯成為Native Code。同時又因為用戶的線程需要執行解決用戶的請求,因此它也會佔用大量的CPU,並且會搶佔編譯線程所使用的CPU,這樣就會導致編譯線程無法儘快地把這些熱點方法編譯到Native Code,使得應用程序長時間運行在解釋執行的狀態,降低服務的質量。同時,服務的RT會增加,服務所使用的CPU也會非常的高,這就是在真實場景中所遇到應用程序預熱的問題,接下來我們來看一下阿里巴巴Dragonwell 8中的JWarmup特性是如何解決這個問題。
二、JWarmup功能
上方為JWarmup的流程圖,它將應用程序的發佈分成了兩個階段,分別是Recording和Replaying。在Recording階段,JVM會接受線上的請求,同時記錄JVM即時編譯器它所編譯方法的信息,並且將這些信息都輸出到一個文件之中。
等到第二次再去啟動的時候,JVM就可以去讀取剛剛所記錄的這些方法編譯的信息,同時會主動的觸發即時編譯器編譯剛剛記錄的熱點方法,使得在用戶請求到來之前,就把熱點方法編譯成為性能較高的Native Code,避免了在用戶請求大量進入的時候做編譯,這樣就能夠進一步提高應用程序的性能,節約CPU使用率。
三、案例演示
(一)Demo代碼
下面根據一個簡單的例子展示如何使用JWarmup功能。
上方為一個簡單的 Java程序,在這個程序之中有一個循環用來模擬線上應用的一個熱點,循環會反覆調用一個方法。
(二)Recording
如何去使用JWarmup的記錄功能Recording?
- 首先,從http://dragonwell-jdk.io/下載Dragonwell 8;
- 添加JVM參數啟動JWarmup的記錄功能:
-XX:+CompilationWarmUpRecording
-XX: CompilationWarmUpRecording=30
-XX: CompilationWarmUpLogfile=./jwarmup.log
-XX:-ClassUnloading
第一個參數表示去打開JWarmup的記錄功能;
第二個參數表示需要記錄的時間,在當前Demo之中選擇記錄30秒;
第三個參數表示記錄編譯信息生成文件的路徑,在這個Demo中,我們將這個文件生成在當前目錄下的jwarmup.log這個文件;
第四個參數是由於JWarmup的Recording功能不支持ClassUnloading,所以需要將這一功能關閉。
當設定的記錄時間到了以後,JWarmup會將記錄好的編譯信息輸出到指定的文件之中,同時會在應用程序的輸出中看到以下這樣一條日誌,表明記錄是成功的。
(三)Replaying
如何使用JWarmup的Replaying功能?
- 添加JVM參數:
-XX:+CompilationWarmUp
-XX:+CompilationWarmUpLogfile=./jwarmup.log
-XX:+PrintCompilationWarmUpDetail
第一個參數表示要使用JWarmup的編譯功能;
第二個參數需要指定剛剛記錄的包含編譯信息的文件,在當前Demo之中,就是剛剛所記錄的當前目錄下的jwarmup.log文件;
第三個參數表示我希望JWarmup打印出一些詳細的日誌,幫助我記錄JWarmup工具的一些行為。
當把這些參數配置好以後,將服務啟動,等待一些關鍵的類的加載完成,可以使用jcmd <pid> JWarmup -notify主動觸發Warmup的編譯。
當Warmup編譯完成後,可以在程序的標準輸出中看到下面三條log,就表示這一次Warmup編譯是成功的。
以上就是關於JWarmup的基本介紹,包含JWarmup所需要解決的問題,解決方法,以及用案例講述如何使用JWarmup功能。同時,我們對JWarmup這個功能還做了非常多的改進,形成了我們另外一份工作:JWarmup2。
上方為JWarmup2整體流程圖,可以看到在這份工作之中,我們記錄了更加豐富的信息,去更好的解決應用程序預熱的問題。
首先我們會使用JFR(Java Flight Recorder)統一所記錄的編譯的信息,這些信息也可以形成一個JFR文件,使用JDK官方所提供的Java Mission Control瀏覽所記錄的所有熱點方法的信息。
此外,我們除了記錄一些方法的編譯,還記錄了每一個方法它所依賴的類的信息。這樣子在我們第二步預熱的時候,就可以根據這些依賴的信息,當我們看到一個方法,它所有的依賴的類都被加載了以後,就會自動觸發這個方法的Warmup編譯,避免了人工手動觸發Warmup編譯。
此外,我們還額外記錄了這些方法所有的Profiling信息,這些信息能夠幫助我們在第二步去更好地生成Warmup的代碼,從而進一步提高應用程序的性能。
JWarmup2未來將在阿里巴巴Dragonwell 11中開源,歡迎大家使用。