JVM(基礎(chǔ)篇)

寫在前面


上周老大給安排幾個面試的任務(wù),我一般問兩方面:

  1. 項目經(jīng)驗中解決過的比較有意思的問題又哪些?
  2. HashMap使用的時候需要注意些什么?

大部分情況下都是希望從第二個問題中挖出來一些東西,和其他同事問的一些JVM相關(guān)的問題比還是太弱了,所以一定要學(xué)習(xí)一下。這篇文章主要想搞明白:

  1. 內(nèi)存管理
  2. 代碼執(zhí)行

Java虛擬的內(nèi)容太多了,不是隨便看看書就能學(xué)會的,這里將要總結(jié)的都只是皮毛而已,應(yīng)付一般面試是夠了。

內(nèi)存管理


Java虛擬機在執(zhí)行的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域,大致如下:

內(nèi)存模型

各部分的功能如下:

區(qū)域 功能
程序計數(shù)器 可以看做當(dāng)前線程執(zhí)行字節(jié)碼的行號
虛擬機棧 存放局部變量、操作棧等
本地方法棧 與虛擬機棧類似,不過是服務(wù)于本地方法
存放對象
方法區(qū) 存放類信息、常量、靜態(tài)變量、JIT編譯后的代碼等
運行時常量池 編譯時生成的各種字面量和符號使用
直接內(nèi)存 通過NIO分配的對外內(nèi)存

在內(nèi)存管理部分比較大的一塊內(nèi)容是GC(垃圾回收),所謂垃圾回收就是將垃圾占用的內(nèi)存回收掉。那么第一個問題:什么是垃圾?

  1. 引用計數(shù)算法:被引用次數(shù)為0的對象。
  2. 根搜索算法:從GC Roots沿著引用找不到的對象。

這里都提到了引用,在JDK 1.2之后Java就已經(jīng)對引用的概念進行了擴充,那么第二個問題:有哪些類型的引用?

  1. 強引用:Object o = new Object()這種都是強引用。
  2. 弱引用:還有用但非必須的,在OOM之前被回收。
  3. 軟引用:更弱的引用,在下次GC的時候被回收。
  4. 虛引用:最弱的,唯一的作用是在對象被回收的時候可以收到通知。

這里只有強引用才能對對象的生命周期造成影響。在虛擬機發(fā)展的過程中進化出不少垃圾回收算法,比如:

  1. 標(biāo)記-清除算法
  2. 復(fù)制算法
  3. 標(biāo)記-整理算法
  4. 分代收集算法

在實際中用到的回收器都是這幾種算法的組合,比如從VisualVM中看到的內(nèi)存是這樣的(需要明白各部分都是怎樣互相配合的):

分代回收

整體上來看是分代收集算法,而S0、S1這兩部分可以看做是標(biāo)記-整理算法。那么第三個問題:常見的CMS垃圾回收器的執(zhí)行流程是怎樣的?

  1. 初始標(biāo)記:GC Roots直接關(guān)聯(lián)的對象。
  2. 并發(fā)標(biāo)記:Root Tracing。
  3. 重新標(biāo)記:修復(fù)由于程序運行導(dǎo)致標(biāo)記產(chǎn)生變動。
  4. 并發(fā)清除

具體如下圖所示:

CMS執(zhí)行流程

可以看到只有在初始標(biāo)記和重新標(biāo)記的時候才需要Stop The World,其他都是和用戶線程一起執(zhí)行,并行執(zhí)行的過程會消耗掉一些CPU資源。

代碼執(zhí)行


把Java源碼丟給JVM肯定是不能執(zhí)行的,需要先用javac編譯成class文件才行,那么第一個問題:class文件的結(jié)構(gòu)是怎樣的?

  • 常量池
  • 訪問標(biāo)志
  • 類索引、父類索引和接口索引
  • 字段表
  • 方法表
  • 屬性表

虛擬機規(guī)范并沒有規(guī)定在什么時候要加載類,但是規(guī)定了在遇到new、反射、父類、Main的時候需要初始化完成。整個類的生命周期如下:

類的生命周期

在虛擬機中通過ClassLoader來進行類的加載,這地方需要明白:

  • 兩個類是否相同,除了類名外還需要判斷ClassLoader是否相同。
  • 雙親委派模式并不是一個強制約束。

在類加載完成之后就可以開始執(zhí)行了,和線程運轉(zhuǎn)相關(guān)的東西都放在棧幀中,其結(jié)構(gòu)如下:

屬性 作用/含義
局部變量表 方法參數(shù)及方法內(nèi)部定義的局部變量
操作數(shù)棧 用來被指令操作
動態(tài)連接 指向運行時常量池中該棧幀所屬方法的引用
方法返回地址 上層方法調(diào)用本方法的位置
附加信息 調(diào)試信息等

執(zhí)行中具體調(diào)用哪個方法是個頭疼的問題,需要處理:

  • 靜態(tài)分派:相同名稱、不同參數(shù)類型的方法。
  • 動態(tài)分派:繼承中復(fù)寫的方法。

字節(jié)碼中的指令都是基于棧的操作,比如要完成1+1這樣的計算,對應(yīng)的指令如下:

iconst_1 // 將常量1壓入棧
iconst_1
iadd // 把棧頂?shù)膬蓚€值相加并出棧,然后把結(jié)果放回棧
istore_0 // 將棧頂?shù)闹捣诺骄植孔兞勘淼?個Solt

解釋執(zhí)行的好處是下載后啟動速度快,但是確定也非常明顯:運行速度慢。JIT正是用來解決這個問題的,能夠?qū)?strong>多次調(diào)用的方法、多次執(zhí)行的循環(huán)體編譯成本地代碼。

優(yōu)化是個很好玩的題目,記得在參加一次變成比賽的時候用gcc -O3編譯之后的代碼把printf()都沒輸出了。。在JIT中比較常見的優(yōu)化手段有:

手段 描述
公共子表達式消除 如果一個表達式已經(jīng)計算過了,那么后面不需要重復(fù)計算
數(shù)組范圍檢查消除 并不是必須一次不漏地檢查
方法內(nèi)聯(lián) 把代碼復(fù)制到調(diào)用方法中
逃逸分析 判斷對象是否可能被方法外引用到

程序執(zhí)行一定會涉及到內(nèi)存操作,在Java中定義了八種操作來完成:

操作 含義
lock 把一個變量標(biāo)識為線程獨占狀態(tài)
unlock 釋放變量
read 將變量從主存讀取到工作內(nèi)存
load 將read到的變量值放入工作內(nèi)存中的副本
use 將工作內(nèi)存中的變量傳遞給執(zhí)行引擎
assign 引擎返回的值傳遞給工作內(nèi)存中的副本
store 將工作內(nèi)存中的變量傳遞給主存
write 把從工作內(nèi)存得到的變量寫入主存對應(yīng)的變量中

這里有必要講一下volatile的作用,在使用到的時候能明白下面兩條即可:

  • 保證變量對所有線程是可見的。
  • 禁止指令重排優(yōu)化。

如果Java中所有的操作都需要程序員來控制的話,會有大量的重復(fù)代碼,而且寫起來很累,那么我們可以通過先行發(fā)生原則來判斷并行的兩個操作是否存在沖突:

  • 程序次序規(guī)則:單線程內(nèi)按照程序書寫順序。
  • 管程鎖定規(guī)則:unlock必須在lock之前。
  • volatile變量規(guī)則:寫操作先行發(fā)生于讀操作。
  • 線程啟動規(guī)則:Thread.start()先于線程的其他任意方法。
  • 線程終止規(guī)則:線程中所有的操作都先于對此線程的終止檢測。
  • 線程中斷規(guī)則:interrupt()先于中斷檢測。
  • 對象終結(jié)規(guī)則:對象的初始化完成先于它的finalize()方法。
  • 傳遞規(guī)則:如果A先于B、B先于C,那么A先于C。

Thread的底層實現(xiàn)還是比較麻煩的,但是最起碼應(yīng)該知道Thread的狀態(tài)是如何進行轉(zhuǎn)換:

線程狀態(tài)轉(zhuǎn)換圖

最后,常見的同步方式是synchronized或者aqs的各種實現(xiàn),這里就不講了,因為每個都足夠?qū)懸淮笃?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,157評論 2 375

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

  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,328評論 11 349
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準(zhǔn)確的地方,同時不同JDK版本的...
    高廣超閱讀 15,658評論 3 83
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,726評論 18 399
  • 一、運行時數(shù)據(jù)區(qū)域 Java虛擬機管理的內(nèi)存包括幾個運行時數(shù)據(jù)內(nèi)存:方法區(qū)、虛擬機棧、本地方法棧、堆、程序計數(shù)器,...
    加油小杜閱讀 1,533評論 1 15
  • 面對著眼前的探燈,不由得深深的吸了一口氣,不停的眨著眼睛,兩只手此刻親密無間緊緊互相擁抱著。 從前我總會做一個夢,...
    藍泡芙閱讀 238評論 0 0