您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)關(guān)于JVM的基礎(chǔ)知識有什么呢,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
虛擬機(jī)給人的感覺像是操作系統(tǒng)、編譯器:非常高大上。但是Java程序就跑在上面,遇到問題還得去排查,性能不行還得去優(yōu)化,基礎(chǔ)的知識還是需要的!
內(nèi)存管理
Java虛擬機(jī)在執(zhí)行的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域,大致如下:
各部分的功能如下:
在內(nèi)存管理部分比較大的一塊內(nèi)容是GC(垃圾回收),所謂垃圾回收就是將垃圾占用的內(nèi)存回收掉。那么第一個問題:什么是垃圾?
引用計(jì)數(shù)算法:被引用次數(shù)為0的對象。
根搜索算法:從GC Roots沿著引用找不到的對象。
這里都提到了引用,在JDK 1.2之后Java就已經(jīng)對引用的概念進(jìn)行了擴(kuò)充,那么第二個問題:有哪些類型的引用?
強(qiáng)引用:Object o = new Object()這種都是強(qiáng)引用。
弱引用:還有用但非必須的,在OOM之前被回收。
軟引用:更弱的引用,在下次GC的時候被回收。
虛引用:最弱的,唯一的作用是在對象被回收的時候可以收到通知。
這里只有強(qiáng)引用才能對對象的生命周期造成影響。在虛擬機(jī)發(fā)展的過程中進(jìn)化出不少垃圾回收算法,比如:
標(biāo)記-清除算法
復(fù)制算法
標(biāo)記-整理算法
分代收集算法
在實(shí)際中用到的回收器都是這幾種算法的組合,比如從VisualVM中看到的內(nèi)存是這樣的(需要明白各部分都是怎樣互相配合的):
整體上來看是分代收集算法,而S0、S1這兩部分可以看做是標(biāo)記-整理算法。那么第三個問題:常見的CMS垃圾回收器的執(zhí)行流程是怎樣的?
初始標(biāo)記:GC Roots直接關(guān)聯(lián)的對象。
并發(fā)標(biāo)記:Root Tracing。
重新標(biāo)記:修復(fù)由于程序運(yùn)行導(dǎo)致標(biāo)記產(chǎn)生變動。
并發(fā)清除
具體如下圖所示:
可以看到只有在初始標(biāo)記和重新標(biāo)記的時候才需要Stop The World,其他都是和用戶線程一起執(zhí)行,不要以為這就完美了,并行執(zhí)行的過程會消耗掉一些CPU資源。
代碼執(zhí)行
把Java源碼丟給JVM肯定是不能執(zhí)行的,需要先用javac編譯成class文件才行,那么第一個問題:class文件的結(jié)構(gòu)是怎樣的?
常量池
訪問標(biāo)志
類索引、父類索引和接口索引
字段表
方法表
屬性表
虛擬機(jī)規(guī)范并沒有規(guī)定在什么時候要加載類,但是規(guī)定了在遇到new、反射、父類、Main的時候需要初始化完成。整個類的生命周期如下:
在虛擬機(jī)中通過ClassLoader來進(jìn)行類的加載,這地方需要明白:
兩個類是否相同,除了類名外還需要判斷ClassLoader是否相同。
雙親委派模式并不是一個強(qiáng)制約束。
在類加載完成之后就可以開始執(zhí)行了,和線程運(yùn)轉(zhuǎn)相關(guān)的東西都放在棧幀中,其結(jié)構(gòu)如下:
執(zhí)行中具體調(diào)用哪個方法是個頭疼的問題,需要處理:
靜態(tài)分派:相同名稱、不同參數(shù)類型的方法。
動態(tài)分派:繼承中復(fù)寫的方法。
字節(jié)碼中的指令都是基于棧的操作,比如要完成1+1這樣的計(jì)算,對應(yīng)的指令如下:
iconst_1 // 將常量1壓入棧iconst_1iadd // 把棧頂?shù)膬蓚€值相加并出棧,然后把結(jié)果放回棧istore_0 // 將棧頂?shù)闹捣诺骄植孔兞勘淼?個Solt
解釋執(zhí)行的好處是下載后啟動速度快,但是確定也非常明顯:運(yùn)行速度慢。JIT正是用來解決這個問題的,能夠?qū)⒍啻握{(diào)用的方法、多次執(zhí)行的循環(huán)體編譯成本地代碼。
優(yōu)化是個很好玩的題目,記得在參加一次變成比賽的時候用gcc -O3編譯之后的代碼把printf()都沒輸出了。。在JIT中比較常見的優(yōu)化手段有:
程序執(zhí)行一定會涉及到內(nèi)存操作,在Java中定義了八種操作來完成:
這里有必要講一下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的底層實(shí)現(xiàn)還是比較麻煩的,但是最起碼應(yīng)該知道Thread的狀態(tài)是如何進(jìn)行轉(zhuǎn)換:
最后,常見的同步方式是synchronized或者aqs的各種實(shí)現(xiàn)。
上述就是小編為大家分享的關(guān)于JVM的基礎(chǔ)知識有什么呢了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。