<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>

          工程啟動抖動的排查(JIT)

          共 3625字,需瀏覽 8分鐘

           ·

          2021-08-06 10:20

          作者:Adrian

          來源:SegmentFault 思否社區(qū)

          1. 前言

          本文僅分享自己在工作中遇到的問題時的解決方案和思路,以及排查的過程。重點還是分享排查的思路,知識點其實已經(jīng)挺老了。如有疑問或描述不妥,歡迎賜教。

          2. 問題表象

          在工程啟動的時候,系統(tǒng)的請求會有一波超時,從監(jiān)控來看,JVM 的GC(G1) 波動較大,CPU波動較大,各個業(yè)務使用的線程池波動較大,外部IO耗時增加。

          3. 先說結(jié)論

          由于JIT的優(yōu)化,導致系統(tǒng)啟動時觸發(fā)了熱點代碼的編譯,且為C2編譯,引發(fā)了CPU占用較高,進而引發(fā)一系列問題,最終導致部分請求超時。

          4. 排查過程

          其實知識點就放在那里,如何將實際遇到的問題和知識點聯(lián)系到一起并能更深刻的理解這部分知識,這或許就是排查和分析的意義,然后沉淀為經(jīng)驗,進而成長。

          4.1 最初的排查

          • 其實我們的工程是一個算法排序工程,里面或多或少也加了一些小的模型和大大小小的緩存,而且從監(jiān)控上來看,JVM 的GC 突刺和 CPU 突刺時間極為接近(這也是一個監(jiān)控平臺時間不夠精準的原因)。所以在前期,我耗費了大量精力和時間去排查JVM,GC 的問題。

          • 首先推薦給大家一個網(wǎng)站:https://gceasy.io/ ,真的分析GC日志巨好用。配合以下的JVM參數(shù)打印GC日志:

            -XX:+PrintGC 輸出GC日志
            -XX:+PrintGCDetails 輸出GC的詳細日志
            -XX:+PrintGCTimeStamps 輸出GC的時間戳(以基準時間的形式,你啟動的時候相當于12點,跟真實時間無關(guān))
            -XX:+PrintGCDateStamps 輸出GC的時間戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
            -Xloggc:../logs/gc.log 日志文件的輸出路
          • 因為看到Y(jié)GC嚴重,所以先后嘗試了如下的方法:

            • 調(diào)整JVM 的堆大小。即 -Xms, -Xmx 參數(shù)。無效。

            • 調(diào)整回收線程數(shù)目。即 -XX:ConcGCThreads 參數(shù)。無效。

            • 調(diào)整期望單次回收時間。即 -XX:MaxGCPauseMillis 參數(shù),無效,甚至更慘。

            • 以上調(diào)整混合測試,均無效。

            • 雞賊的方法。在加載模型之后sleep 一段時間,讓GC平穩(wěn),然后再放請求進來,這樣操作之后GC確實有些好轉(zhuǎn),但是剛開始的請求仍然有超時。(當然了,因為問題根本不在GC上)

          4.2 換個思路

          • 根據(jù)監(jiān)控上來看,線程池,外部IO,啟動時都有明顯的RT上升然后下降,而且趨勢非常一致,這種一般都是系統(tǒng)性問題造成的,比如CPU,GC,網(wǎng)卡,云主機超售,機房延遲等等。所以GC既然無法根治,那么就從CPU方面入手看看。

          • 因為系統(tǒng)啟動時JVM會產(chǎn)生大量GC,無法區(qū)分是由于系統(tǒng)啟動還沒預熱好就來了流量還是說無論啟動了多久,流量一來就會出問題。而我之前排查GC 的操作,即加sleep時間,恰好幫我看到了這個問題,因為能明顯的看出,GC波動的時間,和超時的時間,時間點上已經(jīng)差了很多了,那就是說,與GC無關(guān),無論GC已經(jīng)多么平穩(wěn),流量一來,還是要超時。

          4.3 分析利器Arthas

          不得不說,Arthas 真的是一個很好用的分析工具,節(jié)省了很多復雜的操作。

          • Arthas 文檔:

            https://arthas.aliyun.com/doc/quick-start.html

          • 其實要分析的核心還是流量最開始到來的時候,我們的CPU到底做了什么,于是我們使用Arthas分析流量到來時的CPU情況。其實這部分也可以使用top -Hp pid , jstack 等命令配合完成,不展開敘述。

          • CPU情況,僅展示重要部分:


          圖中可以看出C2 CompilerThread 占據(jù)了非常多的CPU資源。

          4.4 問題的核心

          • 那么這個C2 CompilerThread 究竟是什么呢。

          • 《深入理解JAVA虛擬機》其實有對這部分的敘述,這里我就大白話給大家解釋一下。

          • 其實Java在最開始運行的時候,你可以理解為,就是傻乎乎的按照你寫的代碼執(zhí)行下去,稱之為"解釋器",這樣有一個好處,就是很快,Java搞成.class ,很快就能啟動,跑起來了,但是問題也很明顯啊,就是運行的慢,那么聰明的JVM開發(fā)者們做了一件事情,他們?nèi)绻l(fā)現(xiàn)你有一些代碼頻繁的執(zhí)行,那么他們就會在運行期間幫你把這段代碼編譯成機器碼,這樣運行就會飛快,這就是即時編譯(just-in-time compilation 也就是JIT)。但是這樣也有一個問題,就是編譯的那段時間,耗費CPU。而C2 CompilerThread,正是JIT中的一層優(yōu)化(共計五層,C2 是第五層)。所以,罪魁禍首找到了。

          5. 嘗試解決

          • 解釋器和編譯器的關(guān)系可以如下所示:

          • 就像上面說的,解釋器啟動快,但是執(zhí)行慢。而編譯器又分為以下五個層次。

            第 0 層:程序解釋執(zhí)行,默認開啟性能監(jiān)控功能(Profiling),如果不開啟,可觸發(fā)第二層編譯;
            第 1 層:可稱為 C1 編譯,將字節(jié)碼編譯為本地代碼,進行簡單、可靠的優(yōu)化,不開啟 Profiling;
            第 2 層:也稱為 C1 編譯,開啟 Profiling,僅執(zhí)行帶方法調(diào)用次數(shù)和循環(huán)回邊執(zhí)行次數(shù) profiling 的 C1 編譯;
            第 3 層:也稱為 C1 編譯,執(zhí)行所有帶 Profiling 的 C1 編譯;
            第 4 層:可稱為 C2 編譯,也是將字節(jié)碼編譯為本地代碼,但是會啟用一些編譯耗時較長的優(yōu)化,甚至會根據(jù)性能監(jiān)控信息進行一些不可靠的激進優(yōu)化
          • 所以我們可以嘗試從C1,C2編譯器的角度去解決問題。

          5.1 關(guān)閉分層編譯

          增加參數(shù) :-XX:-TieredCompilation -client (關(guān)閉分層編譯,開啟C1編譯)
          • 效果稀爛。

          • CPU使用率持續(xù)高水位(相比于調(diào)整前)。確實沒了C2 thread 的問題,但是猜測由于代碼編譯的不夠C2那么優(yōu)秀,所以代碼持續(xù)性能低下。

          • CPU截圖:

          5.2 增加C2 線程數(shù)

          增加參數(shù) :-XX:CICompilerCount=8 恢復參數(shù):-XX:+TieredCompilation
          • 效果一般,仍然有請求超時。但是會少一些。

          • CPU截圖:

          5.3 推論

          • 其實從上面的分析可以看出,如果繞不過C2,那么必然會有一些抖動,如果繞過了C2,那么整體性能就會低很多,這是我們不愿看見的,所以關(guān)閉C1,C2,直接以解釋器模式運行我并沒有嘗試。

          5.4 最終方案

          • 既然這部分抖動繞不過去,那么我們可以使用一些mock 流量來承受這部分抖動,也可以稱之為預熱,在工程啟動的時候,使用提前錄制好的流量來使系統(tǒng)熱點代碼完成即時編譯,然后再接收真正的流量,這樣就可以做到真實流量不抖動的效果。

          后話

          • 本文著重分享解決和分析的過程,知識點沒有重點分析。更多知識點請看“參考文章”部分。
          • 本文如有問題歡迎各位校正。

          參考文章

          • 【關(guān)于java:-XX:-TieredCompilation到底做什么】https://www.codenong.com/38721235/
          • 【好像是上面那篇文章的原版】
            https://stackoverflow.com/questions/38721235/what-exactly-does-xx-tieredcompilation-do
          • 【C2 Compiler Thread】
            https://blog.csdn.net/chenxiusheng/article/details/74007750
          • 【C2 CompilerThread9 長時間占用CPU解決方案】https://blog.csdn.net/m0_37886429/article/details/105139611
          • 《深入理解Java虛擬機第二版》第四部分的“晚期(運行期)優(yōu)化”
          • 【深入分析JVM中線程的創(chuàng)建和運行原理 || JIT(future)】
            https://www.cnblogs.com/silyvin/p/10228184.html
          • 【HotSpot虛擬機的分層編譯(Tiered Compilation)】
            https://blog.csdn.net/u013490280/article/details/108522427


          點擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動和交流,掃描下方”二維碼“或在“公眾號后臺回復“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

          - END -


          瀏覽 45
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  а 天堂中文在线资源 | 91视频在线免费观看 | 国产精品欧美性爱 | 日韩一级无码免费视频 | 最大香蕉在线视频精品店 |