<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么Java程序會執(zhí)行一段時間后跑的更快?

          共 2808字,需瀏覽 6分鐘

           ·

          2022-06-28 11:27



          對于Java 應用,程序員之間一個認識口口相傳: 

          要看一個Java程序跑的快不快,需要多跑幾次;另外,Java程序跑一段時間之后會快起來。速度甚至能趕上 C/C++程序的速度。


          如果你問為什么跑一段時間就快了呢?

          一般都能聽到 「因為JVM會把調(diào)用次數(shù)多的熱方法編譯再執(zhí)行」的答案。


          更通俗的話來講, JVM 會把熱方法編譯成機器碼,執(zhí)行效率會更高。就像公司或工廠里,對于一項任務,一般老手都比新人更快,因為老手更熟悉嘛。所以招聘要求里你很少會見到指明要新人的,大部分都是要有工作經(jīng)驗的。


          而JVM 將熱方法編譯生成的機器碼,由于是針對當前平臺,當前硬件生成的,對應用具體執(zhí)行情況分析之后進行編譯而成,所以就像老手一樣,能更了解情況,效率當然更高。


          默默在背后做編譯工作的人就是 JIT (Just-In-Time) 編譯器,一般也叫即時編譯器。



          今天我們一起來看看,這越跑越快的背后,JIT 具體是怎樣工作的。


          我們都知道,Java 原生就是解釋型語言,也是解釋執(zhí)行的,怎么又有了編譯執(zhí)行了?

          執(zhí)行 java -version 的時候,我們一般能看到當前 Java 版本號之后,會有一個 mixed mode,說明當前JVM 運行在混合模式之下,即同時包含解釋執(zhí)行和編譯執(zhí)行。我們也可以通過參數(shù)強制執(zhí)行只按一種模式執(zhí)行。各種環(huán)境根據(jù)自己的需要選擇執(zhí)行的方式。


          相比編譯執(zhí)行,解釋執(zhí)行要慢很多,但仍然廣泛在被運用在各種虛擬機中,比如它內(nèi)存占用少,應用啟動時間更短。更關鍵的優(yōu)勢在于它簡單。一種新語言或者一個語言的新特性出現(xiàn)時,在解釋器中能比編譯器實現(xiàn)要快很多。另外,開發(fā)者會考慮到性價比,一些語言特性很難,同時也不值得在實現(xiàn)在編譯器就只使用解釋器。


          開發(fā)實現(xiàn)語言時,使用解釋器只有兩個要求:

          1. 熟悉VM實現(xiàn)語言

          2. 理解新語言特性、語法和語義


          而像在JIT編譯器實現(xiàn)新語言特性,對開發(fā)者有更多的要求:

          • 熟悉目標機器的應用程序二進制接口規(guī)范

          • 把新語言特性映射到這個目標機器的接口運行時

          • 掌握開發(fā)編譯器生成目標機器碼的能力



          而為了應用程序的執(zhí)行效率、運行速度, Java 又特別需要JIT,在運行的適當時候,可以把一些高頻率代碼編譯,換取更好的效率。


          JIT就是通過將熱方法、代碼段編譯生成機器碼的形式,在下次調(diào)用到該方法時,會直接通過vtable中鏈接的機器碼直接執(zhí)行,所以效率是杠杠的。


          那么問題來了,什么樣的方法才算熱方法,怎樣來判斷熱方法?


          對于熱方法的計算,一般虛擬機內(nèi)有以下幾種實現(xiàn)方式:

          • 基于方法的JIT,JVM內(nèi)常用

          • 基于蹤跡的JIT, Dalvik和 TraceMonkey在使用

          • 基于區(qū)域的JIT,HHVM 使用這種形式


          基于方法的JIT中,一般探測熱點方法有基于采樣的熱點探測,即周期性的去檢查線程的調(diào)用棧頂,如果方法經(jīng)常出現(xiàn)在棧頂,那它就是熱點方法。另一種是基于計數(shù)器的熱點探測,這種會給每個方法建立計數(shù)器,用來統(tǒng)計方法的執(zhí)行次數(shù)。超過閾值的就認為是熱點方法。


          當然需要注意的是,這里統(tǒng)計的次數(shù),不是絕對的次數(shù),和我們進行限流和降級時說的類似,都是一個時間周期內(nèi)的相對頻率,如果在此期間沒有超過,就不算,原來的次數(shù)會減少。


          JIT 編譯的代碼,存儲在 Code Cache 的內(nèi)存區(qū)間??臻g是有限的在JVM 啟動的時候,設置了一個固定的最大值,實現(xiàn)形式也是個堆,在分配滿時會停止編譯,類卸載、替換成新版本等也會從 Code Cache中刪除。

          另外,在JVM JIT編譯器中包含C1、C2    兩種編譯器,在具體的編譯過程中,一般是采用分層編譯,再具體使用不同的編譯器,相比C1,C2編譯需要更多的時間,做更多的優(yōu)化等等,像內(nèi)聯(lián)、循環(huán)展開、逃逸分析、鎖消除與合并、棧上替換……

          前面我們大概了解了JIT的原理,也了解到 JIT 編譯后,機器碼執(zhí)行效率更高,那有什么辦法能了解到我們自己的應用里,JIT有沒有執(zhí)行,用的是C1還是C2,對哪些代碼做過編譯和優(yōu)化呢?


          我們有沒有辦法,能知道都有哪些方法被JIT編譯了,哪些方法本來我們想要效率高一些,期待被編譯卻沒被考慮的,能更直觀的知道呢?

          一個辦法是應用啟動時,增加 JVM 參數(shù):

          -XX:+UnlockDiagnosticVMOptions

          -XX:+PrintCompilation

          -XX:+PrintInlining

          -XX:+PrintCodeCache

          -XX:+PrintCodeCacheOnCompilation

          -XX:+TraceClassLoading

          -XX:+LogCompilation

          -XX:LogFile=~/a.log


          然后根據(jù)這些輸出內(nèi)容,以及日志文件里的內(nèi)容,去分析。

          當然,如果真的是肉眼閱讀那可太累了。好在有一個優(yōu)秀的開源工具用于解析日志文件。

          鐺鐺鐺,來了。

          就是它, JITWatch。

          https://github.com/AdoptOpenJDK/jitwatch

          使用 JavaFX 開發(fā)而成,功能很強大。



          你可以 通過 Open Log 直接解析上面輸出的日志文件。  例如一個簡單的應用,打開日志之后,會看到不同包下的內(nèi)容,這里example111 是示例。

              public void jitTest() {        long x = calc();        System.out.println(x);    }
          public long calc() { long sum = 0; for (long i=0; i< 1000000; i++) { sum = plus(sum, i); } return sum; }
          public long plus(long a, long b) { return a + b; }




          在點擊右側(cè)某個JIT編譯過的具體方法后,點擊TriView,會看到生成的節(jié)字碼,以及相應的源碼是如何對應到字節(jié)碼和匯編代碼的。



          點擊Chain,會看到編譯鏈路


          Inline-info 會顯示哪些方法進行了內(nèi)聯(lián)優(yōu)化。

          這里看到的OSR,就是常聽到的棧上替換(On-stack replacement),用于優(yōu)化在解釋器中執(zhí)行時,向后跳轉(zhuǎn)的循環(huán)分支達到某個閾值時就會被編譯。



          JITWatch 還有一個沙箱的環(huán)境,可以用來實驗觀察 JIT的行為,觀察 JVM 里JIT的決策過程。


          有了工具的幫助,我們能更好的理解JIT 對應用優(yōu)化的決策,從而讓應用性能更佳。

          瀏覽 67
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久亚洲AV成人无码国产人妖 | 在线黄色亚洲视频 | 人人爱人人操 | 日本乱伦精品 | 国产吧在线|