深入理解java虛擬機(jī)(一)

1.什么是java虛擬機(jī)?

虛擬機(jī)是一種抽象化的計算機(jī),通過在實(shí)際的計算機(jī)上仿真模擬各種計算機(jī)功能來實(shí)現(xiàn)的。Java虛擬機(jī)有自己完善的硬體架構(gòu),如處理器、堆棧、寄存器等,還具有相應(yīng)的指令系統(tǒng)。JVM屏蔽了與具體操作系統(tǒng)平臺相關(guān)的信息,使得Java程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺上不加修改地運(yùn)行。

  • 看下面一段java代碼
public class HelloWorld {  
        public static void main(String[] args) {  
            System.out.println("HelloWorld");  
        }  
    }  

運(yùn)行上面的代碼,我們可以得到結(jié)果“HelloWorld” ,這個過程中發(fā)生了什么呢?

首先來看一下Java程序的執(zhí)行過程


java_run_Image.png

java命令首先啟動虛擬機(jī)進(jìn)程,虛擬機(jī)進(jìn)程成功啟動后,讀取參數(shù)“HelloWorld”,把他作為初始類加載到內(nèi)存,對這個類進(jìn)行初始化和動態(tài)鏈接(),然后從這個類的main方法開始執(zhí)行。也就是說我們的.class文件不是直接被系統(tǒng)加載后直接在cpu上執(zhí)行的,而是被一個叫做虛擬機(jī)的進(jìn)程托管的。首先必須虛擬機(jī)進(jìn)程啟動就緒,然后由虛擬機(jī)中的類加載器加載必要的class文件,包括jdk中的基礎(chǔ)類(如String和Object等),然后由虛擬機(jī)進(jìn)程解釋class字節(jié)碼指令,把這些字節(jié)碼指令翻譯成本機(jī)cpu能夠識別的指令,才能在cpu上運(yùn)行。

從這個層面上來看,在執(zhí)行一個所謂的java程序的時候,真真正正在執(zhí)行的是一個叫做Java虛擬機(jī)的進(jìn)程,而不是我們寫的一個個的class文件。這個叫做虛擬機(jī)的進(jìn)程處理一些底層的操作,比如內(nèi)存的分配和釋放等等。我們編寫的class文件只是虛擬機(jī)進(jìn)程執(zhí)行時需要的“原料”。這些“原料”在運(yùn)行時被加載到虛擬機(jī)中,被虛擬機(jī)解釋執(zhí)行,以控制虛擬機(jī)實(shí)現(xiàn)我們java代碼中所定義的一些相對高層的操作,比如創(chuàng)建一個文件等,可以將class文件中的信息看做對虛擬機(jī)的控制信息,也就是一種虛擬指令。

JVM體系結(jié)構(gòu)簡介

JVM_Hierarchy_Image.png

1)方法區(qū):在類裝載器加載class文件到內(nèi)存的過程中,虛擬機(jī)會提取其中的類型信息,并將這些信息存儲到方法區(qū)。方法區(qū)用于存儲已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。由于所有線程都共享方法區(qū),因此它們對方法區(qū)數(shù)據(jù)的訪問必須被設(shè)計為是線程安全的。
2)Java棧:Java棧是線程私有的。每當(dāng)啟動一個新線程時,Java虛擬機(jī)都會為它分配一個Java棧。Java棧以幀為單位保存線程的運(yùn)行狀態(tài)。虛擬機(jī)只會直接對Java棧執(zhí)行兩種操作:以幀為單位的壓棧或出棧。當(dāng)線程調(diào)用java方法時,虛擬機(jī)壓入一個新的棧幀到該線程的java棧中。當(dāng)方法返回時,這個棧幀被從java棧中彈出并拋棄。一個棧幀包含一個java方法的調(diào)用狀態(tài),它存儲有局部變量表、操作棧、動態(tài)鏈接、方法出口等信息。
3)堆:存儲Java程序創(chuàng)建的類實(shí)例。所有線程共享,因此設(shè)計程序時也要考慮到多線程訪問對象(堆數(shù)據(jù))的同步問題。
4)本地方法棧:本地方法棧與虛擬機(jī)棧所發(fā)揮的作用是非常相似的,其區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)。任何本地方法接口都會使用某種本地方法棧。當(dāng)線程調(diào)用Java方法時,虛擬機(jī)會創(chuàng)建一個新的棧幀并壓入Java棧。然而當(dāng)它調(diào)用的是本地方法時,虛擬機(jī)會保持Java棧不變,不再在線程的Java棧中壓入新的幀,虛擬機(jī)只是簡單地動態(tài)鏈接并直接調(diào)用指定的本地方法。如果某個虛擬機(jī)實(shí)現(xiàn)的本地方法接口是使用C連接模型的話,那么它的本地方法棧就是C棧。
5)PC寄存器:一個運(yùn)行中的Java程序,每當(dāng)啟動一個新線程時,都會為這個新線程創(chuàng)建一個自己的PC(程序計數(shù)器)寄存器。程序計數(shù)器的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個計數(shù)器來完成。如果線程正在執(zhí)行的是一個Java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是Natvie方法,這個計數(shù)器值則為空(Undefined)。
6)執(zhí)行引擎:解析JVM字節(jié)碼指令,得到執(zhí)行結(jié)果。在《Java虛擬機(jī)規(guī)范》中詳細(xì)地定義了執(zhí)行引擎遇到每條字節(jié)碼指令應(yīng)該處理什么,并且應(yīng)該得到什么,但是沒有規(guī)定執(zhí)行引擎應(yīng)該如何或者采取什么方式處理而得到這個結(jié)果,而是由JVM的實(shí)現(xiàn)廠家去決定。
執(zhí)行引擎也就是執(zhí)行一條條代碼的一個流程,而代碼是包含在方法體內(nèi)的,所以執(zhí)行引擎本質(zhì)就是執(zhí)行一個個方法所串起來的流程,對應(yīng)到OS中一個執(zhí)行流程就是一個Java線程,即每個Java線程就是一個執(zhí)行引擎的實(shí)例。

《深入理解Java虛擬機(jī)》(二)java虛擬機(jī)運(yùn)行時數(shù)據(jù)區(qū)
《深入理解Java虛擬機(jī)》(三)垃圾收集器與內(nèi)存分配策略
# JVM內(nèi)存模型與垃圾回收

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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