Java虛擬機(jī)必知的四大知識要點

作者 | 鄭雨迪
來源 | 極客時間《深入拆解 Java 虛擬機(jī)》

作為一位 Java 程序員,在盡情享受 Java 虛擬機(jī)帶來好處的同時洁灵,我們還應(yīng)該去了解和思考“這些技術(shù)特性是如何實現(xiàn)的”浮还,去了解最底層的原理咕幻。只有熟悉 JVM滨攻,你才能在遇到 OutOfMemory 等異常時,不會束手無策饶唤,不會一臉懵逼地上網(wǎng)找解決辦法徐伐,最后就算改了幾個啟動參數(shù)解決了問題,也還是云里霧里募狂。

這次呵晨,我會從我專欄里提取了學(xué)習(xí) Java 虛擬機(jī)的 X 大知識要點,助力大家深入理解 JVM熬尺,知其然也知其所以然。 不過你在看知識點之前谓罗,最好能問問自己你會怎么回答粱哼,再和我提供的內(nèi)容做對比,這樣子提升會比較明顯檩咱。

第一大知識要點:Java 字節(jié)碼是如何在虛擬機(jī)里運行的揭措?

我將以 HotSpot 虛擬機(jī)為例,從虛擬機(jī)以及底層硬件兩個角度刻蚯,來分享解析绊含。

1、從虛擬機(jī)視角來看

執(zhí)行 Java 代碼首先需要將它編譯而成的 class 文件加載到 Java 虛擬機(jī)中炊汹。加載后的 Java 類會被存放于方法區(qū)中躬充。實際運行時,虛擬機(jī)會執(zhí)行方法區(qū)內(nèi)的代碼讨便。

如果你熟悉 X86 的話充甚,你會發(fā)現(xiàn)這和段式內(nèi)存管理中的代碼段類似。而且霸褒,Java 虛擬機(jī)同樣也在內(nèi)存中劃分出堆和棧來存儲運行時數(shù)據(jù)伴找。不同的是,Java 虛擬機(jī)會將棧細(xì)分為面向 Java 方法的 Java 方法棧废菱,面向用 C++ 寫的 native 方法的本地方法棧技矮,以及存放各個線程執(zhí)行位置的 PC 寄存器。

在運行過程中殊轴,每當(dāng)調(diào)用進(jìn)入一個 Java 方法衰倦,Java 虛擬機(jī)會在當(dāng)前線程的 Java 方法棧中生成一個棧幀,用以存放局部變量以及字節(jié)碼的操作數(shù)旁理。這個棧幀的大小是提前計算好的耿币,而且 Java 虛擬機(jī)不要求棧幀在內(nèi)存空間里連續(xù)分布。

當(dāng)退出當(dāng)前執(zhí)行的方法時韧拒,不管是正常返回還是異常返回淹接,Java 虛擬機(jī)均會彈出當(dāng)前線程的當(dāng)前棧幀十性,并將之舍棄。

2塑悼、從硬件視角來看

Java 字節(jié)碼無法直接執(zhí)行劲适。因此,Java 虛擬機(jī)需要將字節(jié)碼翻譯成機(jī)器碼厢蒜。

在 HotSpot 里面霞势,上述翻譯過程有兩種形式:第一種是解釋執(zhí)行,相當(dāng)于同聲傳譯斑鸦,即每解析一條字節(jié)碼愕贡,便翻譯成機(jī)器碼并執(zhí)行;第二種是即時編譯(Just-In-Time compilation巷屿,JIT),則相當(dāng)于線下翻譯,即將整個方法中所包含的字節(jié)碼統(tǒng)一翻譯成機(jī)器碼后在執(zhí)行篙螟。

前者的優(yōu)勢在于無需等待編譯墅冷,而后者的優(yōu)勢在于實際運行速度更快。HotSpot 默認(rèn)采用混合模式腔彰,綜合了解釋執(zhí)行和即時編譯兩者的優(yōu)點。它會先解釋執(zhí)行字節(jié)碼,而后將其中反復(fù)執(zhí)行的熱點代碼端逼,以方法為單位進(jìn)行即時編譯。

第二大知識要點:Java 虛擬機(jī)是如何加載 Java 類的盐欺?

Java 虛擬機(jī)加載 Java 類的過程可分為加載析二、鏈接以及初始化三大步驟。

加載是指查找字節(jié)流,并且據(jù)此創(chuàng)建類的過程。加載需要借助類加載器,在 Java 虛擬機(jī)中艾岂,類加載器使用了雙親委派模型,即接收到加載請求時氓辣,會先將請求轉(zhuǎn)發(fā)給父類加載器。

鏈接,是指將創(chuàng)建成的類合并至 Java 虛擬機(jī)中,使之能夠執(zhí)行的過程。鏈接還分驗證、準(zhǔn)備和解析三個階段,分別完成“驗證被加載類是否滿足 Java 虛擬機(jī)約束”赏寇,“為被加載類靜態(tài)字段分配內(nèi)存”渠退,以及“將被加載類中的符號引用解析成為實際引用”的工作碎乃。其中,Java 虛擬機(jī)規(guī)范并不要求解析階段一定要在鏈接步驟中完成惠奸。

初始化梅誓,則是為標(biāo)記為常量值的字段賦值,以及執(zhí)行 <clinit> 方法的過程佛南。類的初始化僅會被執(zhí)行一次证九,這個特性被用來實現(xiàn)單例的延遲初始化。

第三大知識要點:Java 虛擬機(jī)是如何進(jìn)行垃圾回收的共虑?

Java 虛擬機(jī)中的垃圾回收器采用可達(dá)性分析來探索所有存活的對象愧怜。它從一系列 GC Roots 出發(fā),邊標(biāo)記邊探索所有被引用的對象妈拌。為了防止在標(biāo)記過程中堆棧的狀態(tài)發(fā)生改變拥坛,Java 虛擬機(jī)采取安全點機(jī)制來實現(xiàn) Stop-The-World 操作蓬蝶,暫停其他非垃圾回收線程。

回收垃圾對象的內(nèi)存共有三種基礎(chǔ)算法猜惋,分別為:會造成內(nèi)存碎片的清除算法丸氛、性能開銷較大的壓縮算法、以及堆使用效率較低的復(fù)制算法著摔。

通常來說缓窜,Java 虛擬機(jī)會采用分代回收的思想,將堆劃分為新生代和老年代谍咆,并且通過在不同代中應(yīng)用不同的垃圾回收算法禾锤。

傳統(tǒng)的做法是將新生代再劃分為 Eden 區(qū)和兩個大小一致的 Survivor 區(qū)。在只針對新生代的 Minor GC 中摹察,Eden 區(qū)和非空 Survivor 區(qū)的存活對象會被復(fù)制到空的 Survivor 區(qū)中恩掷,當(dāng) Survivor 區(qū)中的存活對象復(fù)制次數(shù)超過一定數(shù)值時,它將被晉升至老年代供嚎。

因為 Minor GC 只針對新生代進(jìn)行垃圾回收黄娘,所以在枚舉 GC Roots 的時候,它需要考慮從老年代到新生代的引用克滴。為了避免掃描整個老年代逼争,Java 虛擬機(jī)引入了名為卡表的技術(shù),大致地標(biāo)出可能存在老年代到新生代的引用的內(nèi)存區(qū)域劝赔。

G1 垃圾回收器將堆劃分為多個等大的區(qū)域誓焦,每個區(qū)域都可以充當(dāng) Eden 區(qū),Survivor 區(qū)或者老年代區(qū)望忆。G1 會優(yōu)先收集垃圾最多的區(qū)域罩阵,從而最大化垃圾回收的效益竿秆。這也是 Garbage First 名字的由來启摄。

Java 11 中引入的實驗性垃圾回收器 ZGC,僅在掃描 GC Roots 時請求 Stop-The-World幽钢,暫停應(yīng)用線程歉备。因此,它宣稱可將 GC 暫停時間控制在 10ms 以下匪燕。ZGC 暫時沒有應(yīng)用分代回收的思路蕾羊,將整個堆空間看成一塊,其代價是垃圾回收 CPU 消耗較高帽驯。

第四大知識要點:Java 內(nèi)存模型是什么龟再?

在現(xiàn)代計算機(jī)系統(tǒng)中,代碼通常不會按照書寫順序執(zhí)行尼变。造成這一情況的原因有三個利凑,分別為編譯器的重排序浆劲,處理器的亂序執(zhí)行,以及內(nèi)存系統(tǒng)的重排序哀澈。

以內(nèi)存系統(tǒng)重排序為例牌借,在多處理器體系架構(gòu)下,每個處理器都可能緩存了一部分?jǐn)?shù)據(jù)割按。由于時刻保持緩存數(shù)據(jù)與內(nèi)存數(shù)據(jù)同步的性能代價太大膨报,因此部分體系架構(gòu)可能允許緩存數(shù)據(jù)與內(nèi)存數(shù)據(jù)不同步。這對 Java 程序的影響便是适荣,兩個不同的 Java 線程在同一時間內(nèi)看到的同一塊內(nèi)存地址中的值可能不同现柠。

Java 內(nèi)存模型是針對上述問題而提出的一套規(guī)范,用以允許 Java 程序員更為細(xì)致地定義 Java 程序的內(nèi)存行為束凑。它通過定義了一系列的 happens-before 操作晒旅,讓應(yīng)用程序開發(fā)者能夠輕易地表達(dá)不同線程的操作之間的內(nèi)存可見性。

在遵守 Java 內(nèi)存模型的前提下汪诉,即時編譯器以及底層體系架構(gòu)能夠調(diào)整內(nèi)存訪問操作废恋,以達(dá)到性能優(yōu)化的效果。如果開發(fā)者沒有正確地利用 happens-before 規(guī)則扒寄,那么將可能導(dǎo)致數(shù)據(jù)競爭鱼鼓。

Java 內(nèi)存模型是通過內(nèi)存屏障來禁止重排序的。對于即時編譯器來說该编,內(nèi)存屏障將限制它所能做的重排序優(yōu)化迄本。對于處理器來說,內(nèi)存屏障會導(dǎo)致緩存的刷新操作课竣。

擴(kuò)展閱讀

我的專欄《深入拆解 Java 虛擬機(jī)》已完結(jié)嘉赎,非常感謝在我專欄完結(jié)之前的 16000 多名訂閱用戶,在未了解完整內(nèi)容的情況下于樟,毅然訂閱了我的專欄公条。為不辜負(fù)大家的信任,我?guī)缀趺科獙诙紩罅块喿x HotSpot 的源代碼迂曲,和同事討論實現(xiàn)背后的設(shè)計理念靶橱,在這個過程中,我也發(fā)現(xiàn)了一些 HotSpot 中的 Bug路捧,或年久失修的代碼关霸,又或者是設(shè)計不合理的地方,這大概算作寫專欄和我本職工作重疊的地方吧杰扫。

專欄雖然到此已經(jīng)結(jié)束了队寇,但是并不代表你對 Java 虛擬機(jī)學(xué)習(xí)的停止。我想章姓,專欄的內(nèi)容僅僅是為你打開了 JVM 學(xué)習(xí)的大門佳遣,里面的風(fēng)景炭序,還是需要你自己來探索。在文章的后面苍日,我列出了一系列的 Java 虛擬機(jī)技術(shù)的相關(guān)博客和閱讀資料惭聂,你仍然可以繼續(xù)加餐。

你可以關(guān)注國內(nèi)幾位 Java 虛擬機(jī)大咖的知乎或微信公眾號:

  • R 大相恃,個人認(rèn)為是中文圈子里最了解 Java 虛擬機(jī)設(shè)計實現(xiàn)的人辜纲,你可以關(guān)注他的知乎賬號:

    https://www.zhihu.com/people/rednaxelafx

  • 你假笨,原阿里 Java 虛擬機(jī)團(tuán)隊成員拦耐,現(xiàn) PerfMa CEO耕腾,公眾號:你假笨

  • 江南白衣,唯品會資深架構(gòu)師杀糯,公眾號:春天的旁邊扫俺;

  • 占小狼,美團(tuán)基礎(chǔ)架構(gòu)部技術(shù)專家固翰,公眾號:占小狼的博客

  • 楊曉峰狼纬,前甲骨文首席工程師,公眾號:小肥羊聊 Java骂际。

如果英文閱讀沒問題的話疗琉,你可以關(guān)注:

你也可以關(guān)注

關(guān)于 Java 虛擬機(jī)的演講,以便掌握 Java 的最新發(fā)展動向歉铝。

當(dāng)然盈简,如果對 GraalVM 感興趣的話,你可以訂閱我們團(tuán)隊的博客太示,之后我會考慮逐一進(jìn)行翻譯

https://medium.com/graalvm

至于其他閱讀材料柠贤,你可以參考 R 大的這份書單

https://www.douban.com/doulist/2545443/

或者這個匯總貼

https://github.com/deephacks/awesome-jvm

如果本專欄已經(jīng)激發(fā)了你對 Java 虛擬機(jī)的學(xué)習(xí)熱情,那么我建議你著手閱讀 HotSpot 源代碼类缤,并且回饋 OpenJDK 開源社區(qū)臼勉。這種回饋并不一定是提交 patch,也可以是 bug report 或者改進(jìn)建議等等呀非。

道阻且長坚俗,努力加餐~!

可以說速缆,Java 虛擬機(jī)就是每一位 Java 工程師進(jìn)階加薪的利器艺糜,你想往上升,你想深入技術(shù)尉剩,不想一直停留在簡單開發(fā)毅臊,或者你在做 Java 性能分析管嬉、調(diào)優(yōu)工作時,那么础倍,Java 虛擬機(jī)絕對是一把助力的利劍沟启。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末美浦,一起剝皮案震驚了整個濱河市项栏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌流酬,老刑警劉巖列另,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件页衙,死亡現(xiàn)場離奇詭異,居然都是意外死亡店乐,警方通過查閱死者的電腦和手機(jī)眨八,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門页响,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人栈拖,你說我怎么就攤上這事辱魁∪敬兀” “怎么了锻弓?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵蝌箍,是天一觀的道長。 經(jīng)常有香客問我妓盲,道長杂拨,這世上最難降的妖魔是什么悯衬? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任弹沽,我火速辦了婚禮筋粗,結(jié)果婚禮上策橘,老公的妹妹穿的比我還像新娘。我一直安慰自己丽已,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布买决。 她就那樣靜靜地躺著沛婴,像睡著了一般督赤。 火紅的嫁衣襯著肌膚如雪嘁灯。 梳的紋絲不亂的頭發(fā)上旁仿,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天枯冈,我揣著相機(jī)與錄音尘奏,去河邊找鬼。 笑死炫加,一個胖子當(dāng)著我的面吹牛俗孝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播魄健,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼赋铝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沽瘦?” 一聲冷哼從身側(cè)響起革骨,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎析恋,沒想到半個月后良哲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡助隧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年筑凫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片并村。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡漏健,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出橘霎,到底是詐尸還是另有隱情蔫浆,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布姐叁,位于F島的核電站瓦盛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏外潜。R本人自食惡果不足惜原环,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望处窥。 院中可真熱鬧嘱吗,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绕德,卻和暖如春患膛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背耻蛇。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工踪蹬, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人臣咖。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓跃捣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親夺蛇。 傳聞我的和親對象是個殘疾皇子枝缔,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容