在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何解決JVM解釋器導致應用崩潰的bug

Rokr_wireless_t ? 來源:openEuler ? 作者:宋堯飛 ? 2021-08-27 09:58 ? 次閱讀

編者按:筆者遇到一個非常典型的問題,應用在 X86 正常運行,在 AArch64 上 JVM 就會崩潰。這個典型的 JVM 內部問題。筆者通過分析最終定位到是由于 JVM 中模板解釋器代碼存在 bug 導致在弱內存模型的平臺上 Crash。

在分析過程中,涉及到非常多的 JVM 內部知識,比如對象頭、GC 復制算法操作、CAS 操作、字節碼執行、內存序等,希望對讀者有所幫助。本文介紹了一般分析 JVM crash 的方法,并且深入介紹了為什么在 aarch64 平臺上引起這樣的問題,最后還給出了修改方法并推送到上游社區中。**對于使用非畢昇 JDK 的其他 JDK 只有在 jdk8u292、jdk11.0.9、jdk15以后的版本才得到修復,讀者使用時需要注意版本選擇避免這類問題發生。

背景知識

java 程序在發生 crash 時,會生成 hs_err_pid.log 文件,以及 core 文件(需要操作系統開啟相關設置),其中 hs_err 文件以文本格式記錄了 crash 發生位置的小范圍精確現場信息(調用棧、寄存器、線程棧、致命信號、指令上下文等)、jvm 各組件狀態信息(java 堆、jit 事件、gc 事件)、系統層面信息(環境變量、入參、內存使用信息、系統版本)等,精簡記錄了關鍵信息。而 core 文件是程序崩潰時進程的二進制快照,完整記錄了崩潰現場信息,可以使用 gdb 工具來打開 core 文件,恢復出一個崩潰現場,方便分析。

約束

文中描述的問題適用于 jdk8u292 之前的版本。

現象

某業務線隔十天半個月總會報過來 crash 問題,crash 位置比較統一,都是在某處執行 young gc 的上下文中,crash 的直接原因是 java 對象的頭被寫壞了,比如這樣:

3ffdd0c6-f467-11eb-9bcf-12bb97331649.png

而正常的對象頭由 markoop 和 metadata 兩部分組成,前者存放該對象的 hash 值、年齡、鎖信息等,后者存放該對象所屬的 Klass 指針。這里關注的是 markoop,64 位機器上它的具體布局如下:

4031612a-f467-11eb-9bcf-12bb97331649.png

每種布局中每個字段的詳細含義可以在 jdk 源碼 jdk8u/hotspot/src/share/vm/oops/markOop.hpp 中找到,這里簡單給出結論就是 gc 階段一個正常對象頭中的 markoop 不可能是全 0,而是比如這樣:

4079be70-f467-11eb-9bcf-12bb97331649.png

此外,crash 時間上也有個特點:基本每次都發生在程序剛啟動時的幾秒內。

分析

發生 crash 的 java 對象有個一致的特點,就是總位于 eden 區,我們仔細分析了 crash 位置的 gc 過程邏輯,特別是會在 gc 期間修改對象頭的相關源碼更是重點關注對象,因為那塊代碼為了追求性能,使用了無鎖編程

40978e3c-f467-11eb-9bcf-12bb97331649.png

補充介紹一下 CAS(Compare And Swap),CAS 的完整意思是比較并替換,并且確保整個操作原子性。CAS 需要 3 個操作數:內存地址 dst,比較值 cmp,要更新的目標值 value。當且僅當內存地址 dst 上的值跟比較值 cmp 相等時,將內存地址 dst 上的值改寫為 value,否則就什么都不做,其在 aarch64 上的匯編實現類似如下:

40ca60aa-f467-11eb-9bcf-12bb97331649.png

然而我們經過反復推敲,這塊 gc 邏輯似乎無懈可擊,而且位于 eden 區也意味著沒有被 gc 搬移過的可能性,這個問題在很長時間里陷入了停滯……

直到某一天又收到了一個類似的 crash,這個問題才迎來了轉機。在這個 crash 里,也是 java 對象的頭被寫壞了,但特殊的地方在于,頭上的錯誤值是 0x2000,憑著職業敏感,我們猜測這個特殊的錯誤值是否來自這個 java 對象本身呢?這個對象的 Java 名字叫 DynamicByteBuffer,來自某個基礎組件。反編譯得到了問題類 DynamicByteBuffer 的代碼:

4122ad64-f467-11eb-9bcf-12bb97331649.png

再結合 core 信息中其他正常 DynamicByteBuffer 對象的布局,確定了這個特殊的 0x2000 值原本應該位于 segmentSize 字段上,而且從代碼中注意到這個 segmentSize 字段是 final 屬性,意味著其值只可能在實例構造函數中被設置,使用 jdk 自帶的命令 javap 進行反匯編,得到對應的字節碼如下:

414aef18-f467-11eb-9bcf-12bb97331649.png

putfield 這條字節碼的作用是給 java 對象的一個字段賦值,在紅框中的語義就是給 DynamicByteBuffer 對象的 segmentSize 字段賦值。

分析到這里,我們做一下小結,crash 的第一現場并非在 gc 上下文中,而是得往前追溯,發生在這個 java 對象被初始化期間,這期間在初始化它的 segmentSize 字段時,因為某種原因,0x2000 被寫到了對象頭上。

接下來繼續分析, JDK 在發生 crash 時會自動生成的 hs_err 日志,其中有記錄最近發生的編譯事件 “Compilation events (250 events)”,從中沒有發現 DynamicByteBuffer 構造函數相關的編譯事件,所以可以推斷 crash 時 DynamicByteBuffer 這個類的構造函數尚未被編譯過(由于 crash 發生在程序啟動那幾秒,JIT 往往需要預熱后才會介入,所以可以假設記錄的比較完整),這意味著,它的構造函數只會通過模板解釋器去執行,更具體地說,是去執行模板解釋器中的 putfield 指令來把 0x2000 寫到 segmentSize 字段位置。

具體怎么寫其實很簡單,就是先拿到 segmentSize 字段的偏移量,根據偏移量定位到寫的位置,然后寫入。然而 JVM 的模板解釋器在實現這個 putfield 指令時,額外增加了一條快速實現路徑,在 runtime 期間會自動(具體的時間點是 “完整” 執行完第一次 putfield 指令后)從慢速路徑切到快速路徑上,這個切換操作的實現全程沒有加鎖,同步完全依賴 barrier。

注:圖中 bcp 指的是 bytecode pointer,就是讀字節碼。

上圖表示接近同一時間點前后,兩條并行流分別構建一個 DynamicByteBuffer 類型的對象過程中,各自完成 segmentSize 字段賦值的過程,用 Java 代碼簡單示意如下:

41ef86f4-f467-11eb-9bcf-12bb97331649.png

其中第一條執行流走的慢速路徑,第二條走的快速路徑,可以留意到,紅色標識的是幾次公共內存的訪存操作,barrier 就分布在這些位置前后(標在下圖中)。

接下來再給一個更加精確一點的指令流模型

簡單介紹一下這個設計模型:

線程從記錄了指令的內存地址 bcp(bytecode pointer) 上取出指令,然后跳轉到該指令地址上執行,當取出的指令是 bcp1(比如 putfeild 指令的慢速路徑)時就是圖中左邊的指令流;

左邊的指令流就是計算出字段的 offset 并 str 到指定內存地址,然后插入 barrier,最后將 bcp2 指令(比如 putfeild 指令的快速路徑)覆寫到步驟 1 中的內存地址 addr 上;

后續線程繼續執行步驟 1 時,由于取出的指令變成了 bcp2,就改為跳轉到圖中右邊的指令流;

右邊的指令流就是直接取出步驟 2 中已經存到指定內存地址中的 offset。

回顧整個設計模型,左邊的指令流通過一個等效于完整 dmb 的 barrier 來保證 str offset 和 str bcp2 這兩條 str 指令的執行順序并且全局可見;而右邊的指令流中,ldr bcp 和 ldr offset 這兩條 ldr 指令之間沒有任何 barrier,設計者可能認為一個無條件跳轉指令可以為兩條 ldr 指令建立依賴,從而保證執行順序,然而從實測結果來看是不成立的。

這里先來簡單補充介紹一下內存順序模型的概念,現代 CPU 為了提高執行效率,在指令的執行順序上擁有很大的自主權,對每個獨立的 CPU 來說,只要確保語義不變,實際如何執行都有可能,這種方式對于單個 CPU 來說沒有問題,當放到多個 CPU 共享數據的時候,這種亂序執行的行為就會引發每個 CPU 看到數據的順序不一致問題,導致跨 CPU 的程序邏輯亂套了。這就需要對讀、寫內存指令進行約束,來規范每個 CPU 看到的內存生效行為,由此提出了內存順序模型的概念:

其中 ARM 采用的是一種弱內存模型,這種模型默認對讀、寫指令沒有任何約束,需要由程序員自己通過插入 barrier 來手動保證。

再回到這個問題上,測試方式是在 ldr offset 指令后額外加了檢測指令:

就是檢查 offset 值是否為 0,如果為 0 則直接強制 crash(設計上保證了 java 對象的任何實例字段的 offset 不可能是 0)。

經過長時間測試,程序果然在這個位置觸發了 crash!這說明上面提到的兩條 ldr 指令不存在依賴關系,或者說這種依賴關系類似 ARMv8 手冊中描述的條件依賴,并不能保證執行順序。ldr offset 指令先于 ldr bcp 執行,使得讀到一個非法的 offset 值 0。更說明了,這才是這個案例的第一案發現場!

找到了問題的根因后,解決方法也就順利出爐了,那就是在兩條 ldr 指令之間插入 barrier 來確保這兩條 ldr 指令不發生亂序。實測證明,這種修復方案非常有效,這類 crash 現象消失。

詳細的修復 patch 見 https://hg.openjdk.java.net/jdk/jdk/rev/b9529fcbbd33 。目前已經 backport 到 jdk8u292、jdk11.0.9、jdk15。

總結

Java 虛擬機 (JVM) 為了追求性能,大量使用了無鎖編程進行設計,而且這么多年以來 JDK(特別是 JDK8)主要都是面向 X86 平臺開發的,如今才慢慢的開始支持 aarch64 平臺,所以 aarch64 弱內存序問題是我們面臨的一個比較嚴峻的挑戰。

后記

如果遇到相關技術問題(包括不限于畢昇 JDK),可以進入畢昇 JDK 社區查找相關資源(點擊原文進入官網),包括二進制下載、代碼倉庫、使用教學、安裝、學習資料等。畢昇 JDK 社區每雙周周二舉行技術例會,同時有一個技術交流群討論 GCC、LLVM、JDK 和 V8 等相關編譯技術,感興趣的同學可以添加如下微信小助手,回復 Compiler 入群。

責任編輯:haq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 應用
    +關注

    關注

    2

    文章

    439

    瀏覽量

    34440
  • JVM
    JVM
    +關注

    關注

    0

    文章

    160

    瀏覽量

    12533

原文標題:一個 JVM 解釋器 bug 在 AArch64 平臺導致應用崩潰的問題分析

文章出處:【微信號:wireless-tag,微信公眾號:啟明云端科技】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦
    熱點推薦

    如何避免存儲示波器再次崩潰?

    ≥300W)。 電源濾波 措施:在電源輸入端加裝EMI濾波(如Tripp Lite ISOBAR),抑制高頻干擾,避免因電源噪聲導致系統崩潰。 2. 硬件散熱與維護 散熱優化 清潔周期:每季度清理
    發表于 05-23 14:47

    服務數據恢復—raid5陣列中硬盤壞道導致陣列崩潰的數據恢復案例

    文件。 存儲中的數據包括:數十臺iunx系統虛擬機和windows系統虛擬機、壓縮包文件、配置文件。 服務存儲故障: raid5陣列中多塊硬盤出現問題,陣列崩潰,數據丟失。
    的頭像 發表于 03-28 13:25 ?254次閱讀
    服務<b class='flag-5'>器</b>數據恢復—raid5陣列中硬盤壞道<b class='flag-5'>導致</b>陣列<b class='flag-5'>崩潰</b>的數據恢復案例

    服務數據恢復—多塊硬盤離線導致EVA存儲崩潰的數據恢復案例

    一臺HP EVA存儲中有23塊硬盤,掛接到一臺windows server操作系統的服務。 EVA存儲上有三個硬盤指示燈亮黃燈,此刻存儲還能正常使用。管理員在更換硬盤的過程中,又出現一塊硬盤對應的指示燈亮黃燈,存儲崩潰,無法使用了。
    的頭像 發表于 12-03 13:32 ?441次閱讀
    服務<b class='flag-5'>器</b>數據恢復—多塊硬盤離線<b class='flag-5'>導致</b>EVA存儲<b class='flag-5'>崩潰</b>的數據恢復案例

    如何使用Ozone分析Cortex-M異常

    Ozone可以幫助用戶快速分析和查找導致CPU故障的軟件bug。本文解釋如何使用Ozone的調試功能,深入了解Cortex-M架構上的這些錯誤。
    的頭像 發表于 11-29 11:14 ?1535次閱讀
    如何使用Ozone分析Cortex-M異常

    變頻主電路的故障如何解決 | 導致變頻主板故障的原因是什么

    的可能性。使這類故障大大減少。 原文標題:變頻主電路的故障如何解決 | 導致變頻主板
    的頭像 發表于 11-20 15:20 ?838次閱讀

    從原理聊JVM(一):染色標記和垃圾回收算法

    導讀 JAVA簡單易用的特性,能夠讓研發人員在不了解JVM的底層運行機制的情況下依舊能夠編寫出功能完善的代碼。 但是對JVM的理解,是一個程序員普通和優秀的分水嶺。全面地了解JVM的工作原理,能夠
    的頭像 發表于 08-20 15:25 ?481次閱讀
    從原理聊<b class='flag-5'>JVM</b>(一):染色標記和垃圾回收算法

    CAN通過模擬開關進行CAN1和CAN2總線的切換,接入總線后導致整個CAN網絡崩潰怎么解決?

    CAN通過模擬開關進行CAN1和CAN2總線的切換, 應用中該模塊未上電,接入總線后導致整個CAN網絡崩潰,測量CAN電平是遠低于正常電平,拿出該模塊CAN網絡恢復正常 給該模塊先上電,再接
    發表于 08-16 06:16

    遇見一個編譯優化導致bug

    最近在調試 can 通信,因為 c8t6 flash 很小,而魚鷹培訓工程完成的驅動越來越多,導致 flash 不足,因此把 bsp 的優化級別設置成 -O2,誰知道在串口輸入數據時直接 hardfault 了。
    的頭像 發表于 08-12 17:26 ?588次閱讀
    遇見一個編譯優化<b class='flag-5'>導致</b>的<b class='flag-5'>bug</b>

    聊聊JVM如何優化

    進行優化。 1.JVM內存模型 針對JAVA8的模型進行討論,JVM的內存模型主要分為幾個關鍵區域:堆、方法區、程序計數、虛擬機棧和本地方法棧。堆內存進一步細分為年輕代、老年代,年輕代按其特性又分為E區,S1和S2區。關于內存
    的頭像 發表于 08-05 17:49 ?724次閱讀
    聊聊<b class='flag-5'>JVM</b>如何優化

    spi_flash期間的計時中斷導致崩潰怎么解決?

    : spi_flash_erase_sector(...); spi_flash_write(...); 如果在閃存訪問期間發生計時中斷,ESP 似乎會崩潰并重新啟動。 當然,這可以通過在訪問 Flash 時禁用中斷來
    發表于 07-12 11:54

    服務數據恢復—raid5陣列熱備盤未完全激活導致陣列崩潰的數據恢復案例

    服務存儲數據恢復環境: 北京某企業一臺EMC FCAX-4存儲上搭建一組由12塊成員盤的raid5磁盤陣列,其中包括2塊熱備盤。 服務存儲故障: raid5陣列中兩塊硬盤離線,熱備盤只有一塊成功激活,raid癱瘓,上層LUN無法使用,存儲
    的頭像 發表于 07-12 10:58 ?529次閱讀

    服務數據恢復—raid5陣列硬盤壞道導致raid崩潰的數據恢復案例

    raid5陣列有23塊成員盤)。存儲系統上層一共分了11個卷。 服務存儲故障: 磁盤故障導致27盤RAID5陣列崩潰,存儲不可用,該存儲設備已經過保。
    的頭像 發表于 07-05 12:00 ?833次閱讀

    助力程序員告別困擾已久的夢魘-Bug

    程序員的噩夢是什么?不用懷疑,就是讓你加班到崩潰Bug!下面是經過業界大佬們“長期加班”積累的小妙招,助力你離早下班又進一步~一、定位Bug范圍及性質要有效解決問題,首先要縮小范圍,集中關注最近
    的頭像 發表于 07-02 08:10 ?653次閱讀
    助力程序員告別困擾已久的夢魘-<b class='flag-5'>Bug</b>

    ESP32-S2重復插拔USB,會導致USB驅動崩潰怎么處理?

    :https://github.com/leeebo/esp32s2_usb 僅保留USB MSC以及VFS部分,以實現類似U盤的功能,測試中發現重復插拔USB有幾率導致USB驅動崩潰。 日志見下圖(左側為
    發表于 06-21 11:53

    IDF4.2.1的編譯優化bug導致panic怎么處理?

    這個假設引起了bug! 我觀察的現象為:uart.c里有三處代碼調用了uart_ll_is_tx_idle內聯函數,但是在編譯配置為-Os的情況下,它自作聰明地把uart_ll_is_tx_idle
    發表于 06-21 10:55
    主站蜘蛛池模板: 久久综合成人网 | 午夜欧美在线 | 最新版天堂资源中文官网 | 久久久久久久久综合 | 永久在线观看www免费视频 | 中文字幕日本一区波多野不卡 | 午夜免费看视频 | 377p亚洲欧洲日本大胆色噜噜 | bt天堂在线www最新版资源网 | 日日操狠狠操 | 超黄视频网站 | 亚洲va国产va天堂va久久 | 午夜大片免费完整在线看 | 久久99精品国产麻豆宅宅 | 日本色午夜 | 年轻人影院www你懂的 | 亚瑟 国产精品 | 久久天天躁狠狠躁夜夜不卡 | 最近2018中文字幕免费看手机 | 亚洲欧美视频在线观看 | 神马影院午夜在线 | 香蕉色综合 | 欧美大胆一级视频 | 亚洲一区二区免费看 | 午夜精品网站 | 久久天天躁夜夜躁狠狠85麻豆 | se色成人亚洲综合 | 精品免费久久久久久成人影院 | 91网站网站网站在线 | 日本免费xxxx色视频 | 成人在线网| 亚洲精品一卡2卡3卡三卡四卡 | 成人免费一区二区三区 | 手机看片国产免费永久 | 在线观看视频一区二区 | 久久久蜜桃 | 亚洲高清视频一区 | 天堂中文在线资源 | 午夜剧场刺激性爽免费视频 | 手机在线看福利 | 日本在线视频不卡 |