關(guān)注有情懷的?一只藍(lán)色猿?今天
閱讀本文需要6分鐘
說在前面的話
作為一名想要在Java鄰域深入的猿猿們碗暗,光知道怎么使用JavaAPI是遠(yuǎn)遠(yuǎn)不夠的益涧,JVM一直是Java進(jìn)階知識(shí)體系中的重要的部分,并且是不可避免的話題主之。下面通過接地氣的描述與插圖帶你理解JVM底層是如何進(jìn)行工作的:
JVM的運(yùn)行流程
我想只要是學(xué)過Java的一定聽過這句話吧:“Write?Once,Run Anywhere ”(編譯一次,隨處運(yùn)行)便贵,那么它是如何實(shí)現(xiàn)隨處運(yùn)行的呢?帶著疑問我們一起看看下面這張圖吧:
由上圖可知:java程序經(jīng)過一次編譯之后冗荸,將java代碼編譯為字節(jié)碼也就是class文件承璃,然后在不同的操作系統(tǒng)上依靠不同的java虛擬機(jī)進(jìn)行解釋,最后再轉(zhuǎn)換為不同平臺(tái)的機(jī)器碼蚌本,最終得到執(zhí)行盔粹。這就是Java代碼運(yùn)行的基本原理隘梨,那么了解了這個(gè)基本原理后,我們嘗試著去做更深的探究舷嗡,那么一個(gè)普通的java程序它的執(zhí)行流程到底是怎樣的呢轴猎?例如我們寫了一段這樣的代碼:
public class HelloWorld { public static void main(String[] args) { System.out.print("Hello world"); } }
這段程序從編譯到運(yùn)行,最終打印出“Hello world”中間經(jīng)過了哪些步驟呢进萄?我們直接上圖:
java代碼通過編譯之后生成字節(jié)碼文件(class文件)捻脖,通過:java HelloWorld執(zhí)行,此時(shí)java根據(jù)系統(tǒng)版本找到j(luò)vm.cfg中鼠,各位可以搜索一下自己電腦上的jvm.cfg文件在哪可婶,它會(huì)根據(jù)你的系統(tǒng)版本放在不同的位置,比如我的這個(gè)文件就在:C:\Program Files\Java\jdk1.8.0_101\jre\lib\amd64\jvm.cfg援雇,打開看一下:
這是我電腦上的文件矛渴,其中-server KNOWN就表示名稱為server的jvm可用。如果這時(shí)你搜索一下你電腦上jvm.dll惫搏,你就會(huì)發(fā)現(xiàn)它一定在你的某個(gè)server目錄下具温,比如我的:C:\Program Files\Java\jdk1.8.0_101\jre\bin\server\jvm.dll。簡(jiǎn)而言之就是通過jvm.cfg文件找到對(duì)應(yīng)的jvm.dll筐赔,jvm.dll則是java虛擬機(jī)的主要實(shí)現(xiàn)桂躏。接下來會(huì)初始化JVM,并且獲取JNI接口,什么是JNI接口川陆,就是java本地接口剂习,你想啊java被編譯成了class文件,JVM怎么從硬盤上找到這個(gè)文件并裝載到JVM里呢较沪,就是通過JNI接口(它還常用于java與操作系統(tǒng)鳞绕、硬件交互),找到class文件后并裝載進(jìn)JVM尸曼,然后找到main方法们何,最后執(zhí)行。
JVM的基本結(jié)構(gòu)
可能通過上面的描述控轿,大家對(duì)JVM運(yùn)行流程有了一個(gè)粗略的認(rèn)識(shí)冤竹,那么JVM內(nèi)部到底是怎么執(zhí)行一個(gè)class文件的呢,也就是上圖中最后一步第6步的內(nèi)部細(xì)節(jié)是怎樣的呢茬射?要了解這個(gè)問題鹦蠕,我們首先得看一下JVM的內(nèi)部結(jié)構(gòu):
從這個(gè)結(jié)構(gòu)不難看出,class文件被jvm裝載以后在抛,經(jīng)過jvm的內(nèi)存空間調(diào)配钟病,最終是由執(zhí)行引擎完成class文件的執(zhí)行。當(dāng)然這個(gè)過程還有其他角色模塊的協(xié)助,這些模塊協(xié)同配合才能讓一個(gè)java程序成功的運(yùn)行肠阱,下面就詳細(xì)介紹這些模板票唆,它們也是后面學(xué)習(xí)jvm最重要的部分。
JVM的內(nèi)存空間
JVM內(nèi)存空間包含:方法區(qū)屹徘、java堆走趋、java棧、本地方法棧噪伊。
方法區(qū):方法區(qū)是各個(gè)線程共享的區(qū)域奏寨,存放類信息理澎、常量窖维、靜態(tài)變量颜武。
java堆:java堆也是線程共享的區(qū)域,我們的類的實(shí)例就放在這個(gè)區(qū)域拙寡,可以想象你的一個(gè)系統(tǒng)會(huì)產(chǎn)生很多實(shí)例授滓,因此java堆的空間也是最大的。如果java堆空間不足了肆糕,程序會(huì)拋出OutOfMemoryError異常般堆。
java棧:java棧是每個(gè)線程私有的區(qū)域,它的生命周期與線程相同诚啃,一個(gè)線程對(duì)應(yīng)一個(gè)java棧淮摔,每執(zhí)行一個(gè)方法就會(huì)往棧中壓入一個(gè)元素,這個(gè)元素叫“棧幀”始赎,而棧幀中包括了方法中的局部變量和橙、用于存放中間狀態(tài)值的操作棧,這里面有很多細(xì)節(jié)造垛,我們以后再講魔招。如果java棧空間不足了五辽,程序會(huì)拋出StackOverflowError異常办斑,想一想什么情況下會(huì)容易產(chǎn)生這個(gè)錯(cuò)誤,對(duì)杆逗,遞歸乡翅,遞歸如果深度很深,就會(huì)執(zhí)行大量的方法罪郊,方法越多java棧的占用空間越大蠕蚜。
本地方法棧:本地方法棧角色和java棧類似,只不過它是用來表示執(zhí)行本地方法的排龄,本地方法棧存放的方法調(diào)用本地方法接口波势,最終調(diào)用本地方法庫,實(shí)現(xiàn)與操作系統(tǒng)橄维、硬件交互的目的尺铣。
PC寄存器:PC寄存器,說到這里我們的類已經(jīng)加載了争舞,實(shí)例對(duì)象凛忿、方法、靜態(tài)變量都去了自己改去的地方竞川,那么問題來了店溢,程序該怎么執(zhí)行,哪個(gè)方法先執(zhí)行委乌,哪個(gè)方法后執(zhí)行床牧,這些指令執(zhí)行的順序就是PC寄存器在管,它的作用就是控制程序指令的執(zhí)行順序遭贸。
執(zhí)行引擎:執(zhí)行引擎當(dāng)然就是根據(jù)PC寄存器調(diào)配的指令順序戈咳,依次執(zhí)行程序指令。
到此本文主要介紹了java虛擬機(jī)運(yùn)行的基本流程壕吹,以及java虛擬機(jī)內(nèi)部結(jié)構(gòu)已完畢著蛙。下一篇我們將探究java內(nèi)存模型以及探索java變量的可見性、有序性耳贬、指令重排等問題踏堡。
更多技術(shù)文章,掃碼關(guān)注“一只藍(lán)色猿”V渚ⅰG牦 !