JVM工作原理

JVM的生命周期##

首先分析一下JVM實例和JVM執(zhí)行引擎實例的區(qū)別
JVM實例:JVM實例對應(yīng)了一個獨立運行的java程序
它是進程級別的
JVM執(zhí)行引擎實例則對應(yīng)了屬于用戶運行程序的線程
它是線程級別的

JVM實例的誕生##

當啟動一個Java程序時,一個JVM實例就產(chǎn)生了

JVM實例的運行##

main()作為該程序初始線程的起點,任何其他線程均由該線程啟動。
JVM內(nèi)部有兩種線程:守護線程和非守護線程
main()屬于非守護線程, 守護線程通常由JVM自己使用,java程序也可以標明自己創(chuàng)建的線程是守護線程

JVM實例的消亡

當程序中的所有非守護線程都終止時,JVM才退出;
若安全管理器允許,程序也可以使用Runtime類或者System.exit()來退出

JVM的體系結(jié)構(gòu)##

Paste_Image.png

JVM的內(nèi)部體系結(jié)構(gòu)分為三部分:##

(1)類加載器(ClassLoader)子系統(tǒng)
作用:用來裝載.class文件
(2)執(zhí)行引擎
作用:執(zhí)行字節(jié)碼,或者執(zhí)行本地方法
(3)運行時數(shù)據(jù)區(qū)
方法區(qū),堆,PC寄存器,本地方法棧

JVM的類加載器##

JVM將整個類加載過程劃分為三個步驟:
(1)裝載
裝載過程負責(zé)找到二進制字節(jié)碼并加載至JVM中
JVM通過類名、類所在的包名通過ClassLoader來完成類的加載
同樣,也采用以上三個元素來標識一個被加載了的類:類名+包名+ClassLoader實例ID。
(2)鏈接
鏈接過程負責(zé)對二進制字節(jié)碼的格式進行校驗、 初始化裝載類中的靜態(tài)變量以及解析類中調(diào)用的接口、類。
在完成了校驗后,JVM初始化類中的靜態(tài)變量,并將其值賦為默認值。
最后一步為對類中的所有屬性、方法進行驗證, 以確保其需要調(diào)用的屬性、方法存在,以及具備應(yīng)的權(quán)限(例如public、private域權(quán)限等), 會造成NoSuchMethodError、NoSuchFieldError等錯誤信息。
(3)初始化
初始化過程即為執(zhí)行類中的靜態(tài)初始化代碼、構(gòu)造器代碼以及靜態(tài)屬性的初始化
在四種情況下初始化過程會被觸發(fā)執(zhí)行:

                                            1.調(diào)用了new; 
                                            2.反射調(diào)用了類中的方法; 
                                            3.子類調(diào)用了初始化; 
                                            4.JVM啟動過程中指定的初始化類。

JVM執(zhí)行引擎##

JVM通過執(zhí)行引擎來完成字節(jié)碼的執(zhí)行,在執(zhí)行過程中JVM采用的是自己的一套指令系統(tǒng),每個線程在創(chuàng)建后,都會產(chǎn)生一個程序計數(shù)器(pc)和棧(Stack),其中程序計數(shù)器中存放了下一條將要執(zhí)行的指令,Stack中存放Stack Frame,表示的為當前正在執(zhí)行的方法,每個方法的執(zhí)行都會產(chǎn)生Stack Frame,Stack Frame中存放了傳遞給方法的參數(shù)、方法內(nèi)的局部變量以及操作數(shù)棧,操作數(shù)棧用于存放指令運算的中間結(jié)果,指令負責(zé)從操作數(shù)棧中彈出參與運算的操作數(shù),指令執(zhí)行完畢后再將計算結(jié)果壓回到操作數(shù)棧,當方法執(zhí)行完畢后則從Stack中彈出,繼續(xù)其他方法的執(zhí)行。

JVM運行時數(shù)據(jù)區(qū)##

JVM在運行時將數(shù)據(jù)劃分為了6個區(qū)域來存儲,而不僅僅是大家熟知的Heap區(qū)域,這6個區(qū)域圖示如下:

Paste_Image.png

第一塊: PC寄存器
PC寄存器是用于存儲每個線程下一步將執(zhí)行的JVM指令,如該方法為native的,則PC寄存器中不存儲任何信息
第二塊:JVM棧
JVM棧是線程私有的,每個線程創(chuàng)建的同時都會創(chuàng)建JVM棧,JVM棧中存放的為當前線程中局部基本類型的變量,部分返回結(jié)果以及函數(shù)的參數(shù)值,非基本類型的對象的JVM棧上僅存放一個指向堆上的地址
第三塊:堆(Heap)
Heap是大家最為熟悉的區(qū)域,它是JVM用來存儲對象實例以及數(shù)組值的區(qū)域,可以認為java中所有通過New創(chuàng)建的對象的內(nèi)存都在此分配,Heap中的對象的內(nèi)存需要等待GC進行回收
第四塊:方法區(qū)域(Method Area)
(1)方法區(qū)域存放了所加載的類的信息(名稱、修飾符等)、類中的靜態(tài)變量、類中定義為final類型的常量、類中的Field信息、類中的方法信息,當開發(fā)人員在程序中通過Class對象中的getName、isInterface等方法來獲取信息時,這些數(shù)據(jù)都來源于方法區(qū)域,可見方法區(qū)域的重要性,同樣,方法區(qū)域也是全局共享的,在一定的條件下它也會被GC;當方法區(qū)域需要使用的內(nèi)存超過其允許的大小時,會拋出OutOfMemory的錯誤信息。
(2)在Sun JDK中這塊區(qū)域?qū)?yīng)的為Permanet Generation,又稱為持久代,默認為64M,可通過-XX:PermSize以及-XX:MaxPermSize來指定其大小。
第五塊:運行時常量池(Runtime Constant Pool)
類似C中的符號表,存放的為類中的固定的常量信息、方法和Field的引用信息等,其空間從方法區(qū)域中分配。
第六塊:本地方法堆棧(Native Method Stacks)
JVM采用本地方法堆棧來支持native方法的執(zhí)行,此區(qū)域用于存儲每個native方法調(diào)用的狀態(tài)

JVM的垃圾回收問題##

GC的基本原理:
為將內(nèi)存中不再被使用的對象進行回收,GC中用于回收內(nèi)存中不被使用的對象的方法稱為收集器
由于GC需要消耗一些資源和時間的,Java在對對象的生命周期特征進行分析后,采用了分代的方式來進行對象的收集,即按照新生代,舊生代的方式來對對象進行收集
(1)對新生代的對象的收集稱為Minor GC
(2)對舊生代的對象的收集稱為Full GC
(3)程序中主動調(diào)用System.gc()強制執(zhí)行的GC為Full GC
JVM中自動內(nèi)存回收機制
(1)引用計數(shù)收集器
原理:引用計數(shù)是標識Heap中對象狀態(tài)最明顯的一種方法
引用計數(shù)的方法簡單來說就是對每一個對象都提供一個關(guān)聯(lián)的引用計數(shù),以此來標識該對象是否被使用,當這個計數(shù)為零時,說明這個對象已經(jīng)不再被使用了
優(yōu)點:
引用計數(shù)的好處是可以不用暫停應(yīng)用,當計數(shù)變零時,即可將此對象的內(nèi)存空間回收,但它需要給每個對象附加一個關(guān)聯(lián)引用計數(shù)
缺點:
引用計數(shù)無法解決循環(huán)引用的問題,因此JVM并沒有采用引用計數(shù)
(2)跟蹤收集器
原理:
跟蹤收集器的方法為停止應(yīng)用的工作,然后開始跟蹤對象,跟蹤時從對象根開始沿著引用跟蹤,直到檢查完所有的對象
根對象的來源主要有三種:
1.被加載的類的常量池中的對象引用
2.傳到本地方法中,沒有被本地方法"釋放"的對象引用
3.虛擬機運行時數(shù)據(jù)區(qū)從垃圾收集器的堆中分配的部分
存在問題:
跟蹤收集器采用的均為掃描的方法,
但JVM將Heap分為了新生代和舊生代, 在進行minor GC時需要掃描是否有舊生代引用了新生代中的對象, 但又不可能每次minor GC都掃描整個舊生代中的對象,
因此JVM采用了一種稱為卡片標記(Card Marking)的算法來避免這種現(xiàn)象。
(3)卡片標記算法
卡片標記的算法為將舊生代以某個大?。ɡ?12字節(jié))進行劃分,劃分出來的每個區(qū)域稱為卡片, JVM采用卡表維護卡的狀態(tài),每張卡片在卡表中占用一個字節(jié)的標識(有些JVM實現(xiàn)可能會不同), 當Java代碼執(zhí)行過程中發(fā)現(xiàn)舊生代的對象引用或釋放了對于新生代對象的引用時, 就相應(yīng)的修改卡表中卡的狀態(tài),每次Minor GC只需掃描卡表中標識為臟狀態(tài)的卡中的對象即可

Paste_Image.png

JVM中將對象的引用分為四種類型,不同的對象引用類型會造成GC采用不同的方法進行回收:
(1)強引用:默認情況下,對象采用的均為強引用(這個對象的實例沒有其他對象引用,GC時才會被回收)
(2)軟引用:軟引用是java中提供的一種比較適合于緩存場景的應(yīng)用(只有在內(nèi)存不夠用的情況下才會被GC)
(3)弱引用:在GC時一定會被GC回收
(4)虛引用:由于虛引用只是用來得知對象是否被GC

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JVM內(nèi)存模型Java虛擬機(Java Virtual Machine=JVM)的內(nèi)存空間分為五個部分,分別是: ...
    光劍書架上的書閱讀 2,775評論 2 26
  • 原文閱讀 前言 這段時間懈怠了,罪過! 最近看到有同事也開始用上了微信公眾號寫博客了,挺好的~給他們點贊,這博客我...
    碼農(nóng)戲碼閱讀 6,157評論 2 31
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準確的地方,同時不同JDK版本的...
    高廣超閱讀 16,056評論 3 83
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,815評論 11 349
  • 那年,記不得是哪一年,只是印象著春風(fēng)吹卷著陣陣丁香的味。我只是孩子,也許天真也許青春,總該就是在那樣一個最愛幻想的...
    眼白閱讀 232評論 0 0

友情鏈接更多精彩內(nèi)容