Zing加快了JVM应用程序的预热
Java虛擬機(jī)(JVM)提供了托管運(yùn)行時(shí)環(huán)境,用于安全部署應(yīng)用程序,其性能通??梢猿^本機(jī)編譯語言(如C和C ++)的性能。 通過即時(shí)(JIT)編譯進(jìn)行垃圾收集和自適應(yīng)編譯的內(nèi)存管理是兩個(gè)最突出的功能。
盡管使用字節(jié)碼和JIT編譯可以提供更好的峰值性能,但是對于某些類型的應(yīng)用程序,達(dá)到該級別所需的預(yù)熱時(shí)間可能會成問題。
在本文中,我們將研究Azul作為Zing JVM的一部分而開發(fā)的一組技術(shù),以解決這些限制。
首先,讓我們看一下在JVM上運(yùn)行時(shí)的典型應(yīng)用程序性能圖。
該圖并不理想,因?yàn)閼?yīng)用程序開始時(shí)性能降低,并且JVM需要時(shí)間才能發(fā)揮其全部潛能。 該圖可以分為三個(gè)不同的部分: 讓我們看一下JVM內(nèi)部每種情況的變化。
- 當(dāng)應(yīng)用程序啟動時(shí),JVM必須加載并初始化必要的類。 完成此操作后,JVM將在main()入口點(diǎn)開始執(zhí)行。 由于JVM是虛擬機(jī) ,因此它不會使用與運(yùn)行它的物理機(jī)相同的指令集。 因此,有必要將類文件的字節(jié)碼轉(zhuǎn)換為物理CPU的指令集。 這稱為字節(jié)碼解釋 。 必須對每個(gè)執(zhí)行的字節(jié)碼重復(fù)此操作,這會導(dǎo)致性能比本地編譯的應(yīng)用程序低得多。 這在很大程度上歸因于Java首次發(fā)布時(shí)速度慢的聲譽(yù)。
上圖以黃色顯示了已解釋的模式。
- 為了減輕在解釋模式下運(yùn)行的問題,JVM在內(nèi)部記錄了每種方法調(diào)用頻率的統(tǒng)計(jì)信息。 這樣,它就可以為重復(fù)調(diào)用的方法(例如,長時(shí)間運(yùn)行的循環(huán))中的代碼識別熱點(diǎn) (因此稱為Oracle JVM)。 當(dāng)方法調(diào)用計(jì)數(shù)達(dá)到定義的閾值時(shí),JVM將方法傳遞給內(nèi)部編譯器,該編譯器稱為即時(shí)編譯器(通常稱為JIT)。
JVM在此階段使用的編譯器稱為C1(以前,它也稱為客戶端編譯器)。 C1 JIT旨在盡快生成代碼,以便快速提高這些方法的性能。 為此,C1將僅應(yīng)用最簡單的優(yōu)化,這些優(yōu)化不需要其他配置數(shù)據(jù)或需要很長時(shí)間才能生成。 如上圖的綠色部分所示,隨著編譯更多方法,性能逐漸提高。 在運(yùn)行此代碼時(shí),JVM將收集有關(guān)如何使用該方法以及如何執(zhí)行代碼的全面分析數(shù)據(jù)。
- 在調(diào)用方法的次數(shù)達(dá)到第二個(gè)閾值時(shí),JVM將使用其他JIT編譯器重新編譯該方法。 在Zing的情況下,這是基于開源LLVM項(xiàng)目的Falcon JIT。 默認(rèn)的OpenJDK二級JIT編譯器為C2,它非常舊且難以增強(qiáng)。
Falcon是比C1更復(fù)雜的編譯器。 它使用在執(zhí)行C1生成的代碼期間收集的概要分析數(shù)據(jù)以及來自JVM的其他內(nèi)部數(shù)據(jù),將最大程度的優(yōu)化應(yīng)用于其生成的代碼。 這是圖形的藍(lán)色部分,一旦所有常用的方法都已編譯,性能最終將達(dá)到最高水平。 此時(shí),該應(yīng)用程序被視為已預(yù)熱 。
現(xiàn)在,我們了解了JIT編譯在JVM中的工作方式,可以采取什么措施來減少其對應(yīng)用程序啟動性能的影響? Azul開發(fā)了兩種技術(shù),使Zing JVM能夠減輕預(yù)熱效果。
關(guān)于如何解決此問題的一個(gè)常見建議是讓應(yīng)用程序運(yùn)行,直到所有常用的方法均已JIT編譯,然后讓JVM將已編譯的代碼寫入文件中。 重新啟動應(yīng)用程序時(shí),可以重新加載以前編譯的代碼,并且應(yīng)用程序?qū)⒁酝V怪暗乃俣冗\(yùn)行。
聽起來不錯(cuò)的解決方案,但有兩個(gè)重大缺點(diǎn):
- 盡管代碼是為正在運(yùn)行的應(yīng)用程序編譯的,但不能保證在重新啟動JVM時(shí)它仍然有效。 為何使用斷言是一個(gè)很好的例子。 如果在禁用斷言的情況下運(yùn)行應(yīng)用程序,則JIT將消除代碼的相關(guān)部分。 如果隨后在啟用斷言的情況下重新啟動應(yīng)用程序并使用先前編譯的代碼,則斷言將丟失。
- JVM規(guī)范有一個(gè)關(guān)于JVM必須如何工作的精確定義。 它包含在Java SE規(guī)范中,該規(guī)范是根據(jù)JCP在相關(guān)JSR中創(chuàng)建的。 這定義了JVM運(yùn)行應(yīng)用程序時(shí)必須執(zhí)行的特定任務(wù)。 必須先顯式加載和初始化類,然后才能使用它們。 同樣,如果使用了先前編譯的代碼,這可能會使JVM的正確操作無效。
Azul的ReadyNow! 這項(xiàng)技術(shù)采用了一種不同的方法,可以確保正在執(zhí)行的代碼和JVM的啟動順序都完全正確。
要實(shí)現(xiàn)此ReadyNow! 記錄正在運(yùn)行的應(yīng)用程序的配置文件。 可以隨時(shí)獲取配置文件,以便用戶可以決定他們的應(yīng)用程序何時(shí)以所需的級別運(yùn)行。 可以拍攝多個(gè)配置文件快照,以便用戶可以在重新啟動應(yīng)用程序時(shí)選擇所需的配置文件。
該配置文件記錄五段數(shù)據(jù):
再次啟動應(yīng)用程序時(shí),此數(shù)據(jù)將用作JVM的高級知識,以執(zhí)行以下步驟:
- 加載配置文件中列出的所有引導(dǎo)程序和系統(tǒng)類。
- 初始化那些已加載類的安全子集。 被認(rèn)為是安全的類是JMV規(guī)范允許的類。
- 確定所需的類加載器后,將立即加載配置文件中的其他類。 如前所述,由于Java平臺的動態(tài)性質(zhì),這是必需的。
- 分析和推測性優(yōu)化數(shù)據(jù)用于使用Falcon JIT編譯所需的方法。
所有這些都在應(yīng)用程序在main()入口點(diǎn)開始執(zhí)行之前發(fā)生。
這樣的結(jié)果是,當(dāng)應(yīng)用程序開始執(zhí)行時(shí),幾乎所有熱門方法都已使用Falcon JIT進(jìn)行了編譯。 通過使用概要分析數(shù)據(jù),可以對代碼進(jìn)行大量優(yōu)化,并使用已知有效的推測性優(yōu)化(也可以避免不必要的優(yōu)化)。 性能開始于非常接近收集概要文件時(shí)的水平。 由于此過程的工作方式受到一些限制,因此應(yīng)用程序通常僅需要執(zhí)行少量事務(wù)即可使其全速運(yùn)行。
但是,此方法確實(shí)會產(chǎn)生影響。 在應(yīng)用程序何時(shí)可以開始處理事務(wù)之前,JVM還有很多工作要做。
為了減輕這種影響,Azul開發(fā)了Compile Stashing
正如我們已經(jīng)看到的,在重新啟動應(yīng)用程序時(shí),不可能簡單地保存已編譯的代碼然后重新加載它。 但是,可以保存已編譯的代碼,并有效地將其用作緩存。
方法的字節(jié)碼與保存的性能分析數(shù)據(jù)組合在一起,因此可以將它們轉(zhuǎn)換為編譯器使用的中間表示(IR)。 在編譯代碼時(shí),JIT將調(diào)用JVM,以幫助其做出有關(guān)可以使用的優(yōu)化的決策。 例如,要確定是否可以內(nèi)聯(lián)方法,JIT必須首先確定該方法是否可以進(jìn)行虛擬化,這需要查詢JVM。 JIT完成對方法的分析后,便可以最大程度的優(yōu)化對其進(jìn)行編譯。
此過程是完全確定的。 給定相同的方法字節(jié)碼和配置文件數(shù)據(jù)作為輸入以及對JVM的相同查詢集,JIT編譯器的輸出將始終相同。
編譯存儲補(bǔ)充ReadyNow! 除了記錄配置文件外,還將當(dāng)前編譯方法的本機(jī)代碼以及VM回調(diào)的查詢和響應(yīng)寫入文件。 當(dāng)應(yīng)用程序再次啟動時(shí),ReadyNow! 像以前一樣,基于配置文件加載并初始化可以的類。 但是,保存的已編譯方法現(xiàn)在用作緩存,以減少對顯式編譯的需求。 操作流程圖如下所示:
好了! 將IR用于方法的字節(jié)碼,并查詢編譯期間使用的VM的組合,以確定存儲的編譯代碼是否匹配。 如果是這樣,則可以從“編譯存儲區(qū)”返回該代碼。 如果由于某種原因輸入與編譯請求不匹配,則可以像以前一樣將其傳遞給Falcon JIT。 重要的是要注意,使用此技術(shù)不會使JVM規(guī)范中有關(guān)應(yīng)用程序初始化的任何要求無效。
測試表明,使用“編譯存儲”時(shí),ReadyNow!需要的編譯時(shí)間! 最多可減少80%,并最多減少60%的CPU負(fù)載。
如您所見,ReadyNow! 和Compile Stashing通過記錄類加載和概要分析數(shù)據(jù),無效的推測性優(yōu)化和編譯的代碼來解決應(yīng)用程序預(yù)熱時(shí)間的問題。 重新啟動應(yīng)用程序時(shí)使用所有這些組件可以大大減少應(yīng)用程序達(dá)到最佳性能水平所需的時(shí)間和CPU負(fù)載。
Zing是啟動速度快,保持速度快且運(yùn)行速度更快的JVM。
準(zhǔn)備開始使用更好的JVM了嗎?
在您選擇的Linux發(fā)行版上嘗試Zing Free…
翻譯自: https://www.javacodegeeks.com/2019/06/faster-jvm-application-warm-zing.html
總結(jié)
以上是生活随笔為你收集整理的Zing加快了JVM应用程序的预热的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: javafx canvas_JavaFX
- 下一篇: 没有垃圾回收的JVM