www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號(hào)精選 > Linux閱碼場
[導(dǎo)讀]編者按:筆者遇到一個(gè)非常典型的問題,應(yīng)用在X86正常運(yùn)行,在AArch64上JVM就會(huì)崩潰。這個(gè)典型的JVM內(nèi)部問題。筆者通過分析最終定位到是由于JVM中模板解釋器代碼存在bug導(dǎo)致在弱內(nèi)存模型的平臺(tái)上Crash。在分析過程中,涉及到非常多的JVM內(nèi)部知識(shí),比如對(duì)象頭、GC復(fù)制算...

編者按:筆者遇到一個(gè)非常典型的問題,應(yīng)用在 X86 正常運(yùn)行,在 AArch64 上 JVM 就會(huì)崩潰。這個(gè)典型的 JVM 內(nèi)部問題。筆者通過分析最終定位到是由于 JVM 中模板解釋器代碼存在 bug 導(dǎo)致在弱內(nèi)存模型的平臺(tái)上 Crash。在分析過程中,涉及到非常多的 JVM 內(nèi)部知識(shí),比如對(duì)象頭、GC 復(fù)制算法操作、CAS 操作、字節(jié)碼執(zhí)行、內(nèi)存序等,希望對(duì)讀者有所幫助。本文介紹了一般分析 JVM crash 的方法,并且深入介紹了為什么在 aarch64 平臺(tái)上引起這樣的問題,最后還給出了修改方法并推送到上游社區(qū)中。**對(duì)于使用非畢昇 JDK 的其他 JDK 只有在 jdk8u292、jdk11.0.9、jdk15以后的版本才得到修復(fù),讀者使用時(shí)需要注意版本選擇避免這類問題發(fā)生。

背景知識(shí)

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

約束

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

現(xiàn)象

某業(yè)務(wù)線隔十天半個(gè)月總會(huì)報(bào)過來 crash 問題,crash 位置比較統(tǒng)一,都是在某處執(zhí)行 young gc 的上下文中,crash 的直接原因是 java 對(duì)象的頭被寫壞了,比如這樣:

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

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

此外,crash 時(shí)間上也有個(gè)特點(diǎn):基本每次都發(fā)生在程序剛啟動(dòng)時(shí)的幾秒內(nèi)。

分析

發(fā)生 crash 的 java 對(duì)象有個(gè)一致的特點(diǎn),就是總位于 eden 區(qū),我們仔細(xì)分析了 crash 位置的 gc 過程邏輯,特別是會(huì)在 gc 期間修改對(duì)象頭的相關(guān)源碼更是重點(diǎn)關(guān)注對(duì)象,因?yàn)槟菈K代碼為了追求性能,使用了無鎖編程:

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

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

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

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

putfield 這條字節(jié)碼的作用是給 java 對(duì)象的一個(gè)字段賦值,在紅框中的語義就是給 DynamicByteBuffer 對(duì)象的 segmentSize 字段賦值。

分析到這里,我們做一下小結(jié),crash 的第一現(xiàn)場并非在 gc 上下文中,而是得往前追溯,發(fā)生在這個(gè) java 對(duì)象被初始化期間,這期間在初始化它的 segmentSize 字段時(shí),因?yàn)槟撤N原因,0x2000 被寫到了對(duì)象頭上。

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

具體怎么寫其實(shí)很簡單,就是先拿到 segmentSize 字段的偏移量,根據(jù)偏移量定位到寫的位置,然后寫入。然而 JVM 的模板解釋器在實(shí)現(xiàn)這個(gè) putfield 指令時(shí),額外增加了一條快速實(shí)現(xiàn)路徑,在 runtime 期間會(huì)自動(dòng)(具體的時(shí)間點(diǎn)是 “完整” 執(zhí)行完第一次 putfield 指令后)從慢速路徑切到快速路徑上,這個(gè)切換操作的實(shí)現(xiàn)全程沒有加鎖,同步完全依賴 barrier,由于整個(gè)過程比較復(fù)雜,這里首先給一個(gè)比較容易理解的并行流程圖:

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

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

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

接下來再給一個(gè)更加精確一點(diǎn)的指令流模型:

簡單介紹一下這個(gè)設(shè)計(jì)模型:

  1. 線程從記錄了指令的內(nèi)存地址 bcp(bytecode pointer) 上取出指令,然后跳轉(zhuǎn)到該指令地址上執(zhí)行,當(dāng)取出的指令是 bcp1(比如 putfeild 指令的慢速路徑)時(shí)就是圖中左邊的指令流;
  2. 左邊的指令流就是計(jì)算出字段的 offset 并 str 到指定內(nèi)存地址,然后插入 barrier,最后將 bcp2 指令(比如 putfeild 指令的快速路徑)覆寫到步驟 1 中的內(nèi)存地址 addr 上;
  3. 后續(xù)線程繼續(xù)執(zhí)行步驟 1 時(shí),由于取出的指令變成了 bcp2,就改為跳轉(zhuǎn)到圖中右邊的指令流;
  4. 右邊的指令流就是直接取出步驟 2 中已經(jīng)存到指定內(nèi)存地址中的 offset。
回顧整個(gè)設(shè)計(jì)模型,左邊的指令流通過一個(gè)等效于完整 dmb 的 barrier 來保證 str offset 和 str bcp2 這兩條 str 指令的執(zhí)行順序并且全局可見;而右邊的指令流中,ldr bcp 和 ldr offset 這兩條 ldr 指令之間沒有任何 barrier,設(shè)計(jì)者可能認(rèn)為一個(gè)無條件跳轉(zhuǎn)指令可以為兩條 ldr 指令建立依賴,從而保證執(zhí)行順序,然而從實(shí)測(cè)結(jié)果來看是不成立的。

這里先來簡單補(bǔ)充介紹一下內(nèi)存順序模型的概念,現(xiàn)代 CPU 為了提高執(zhí)行效率,在指令的執(zhí)行順序上擁有很大的自主權(quán),對(duì)每個(gè)獨(dú)立的 CPU 來說,只要確保語義不變,實(shí)際如何執(zhí)行都有可能,這種方式對(duì)于單個(gè) CPU 來說沒有問題,當(dāng)放到多個(gè) CPU 共享數(shù)據(jù)的時(shí)候,這種亂序執(zhí)行的行為就會(huì)引發(fā)每個(gè) CPU 看到數(shù)據(jù)的順序不一致問題,導(dǎo)致跨 CPU 的程序邏輯亂套了。這就需要對(duì)讀、寫內(nèi)存指令進(jìn)行約束,來規(guī)范每個(gè) CPU 看到的內(nèi)存生效行為,由此提出了內(nèi)存順序模型的概念:

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

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

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

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

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

詳細(xì)的修復(fù) patch 見 https://hg.openjdk.java.net/jdk/jdk/rev/b9529fcbbd33 。目前已經(jīng) backport 到 jdk8u292、jdk11.0.9、jdk15。

總結(jié)

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

后記

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

rc="https://img.21ic.com/weixin/tr/2021-09/02/357miuol149.png">



本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉