Java技術與Java虛擬機器

來源:互聯網
上載者:User

說起Java,人們首先想到的是Java程式設計語言,然而事實上,Java是一種技術,它由四方面組成: Java程式設計語言、Java類檔案格式、Java虛擬機器和Java應用程式介面(Java API)。它們的關係如所示:

 

圖1  Java四個方面的關係

運行期環境代表著Java平台,開發人員編寫Java代碼(.java檔案),然後將之編譯成位元組碼(.class 檔案)。最後位元組碼被裝入記憶體,一旦位元組碼進入虛擬機器,它就會被解譯器解釋執行,或者是被即時代碼發生器有選擇的轉換成機器碼執行。從也可以看出 Java平台由Java虛擬機器和Java應用程式介面搭建,Java語言則是進入這個平台的通道,用Java語言編寫並編譯的程式可以運行在這個平台上。這個平台的結構如所示:

 

在Java平台的結構中, 可以看出,Java虛擬機器(JVM) 處在核心的位置,是程式與底層作業系統和硬體無關的關鍵。它的下方是移植介面,移植介面由兩部分組成:適配器和Java作業系統, 其中依賴於平台的部分稱為適配器;JVM 通過移植介面在具體的平台和作業系統上實現;在JVM 的上方是Java的基本類庫和擴充類庫以及它們的API, 利用Java API編寫的應用程式(application) 和小程式(Java applet) 可以在任何Java平台上運行而無需考慮底層平台, 就是因為有Java虛擬機器(JVM)實現了程式與作業系統的分離,從而實現了Java 的平台無關性。

那麼到底什麼是Java虛擬機器(JVM)呢?通常我們談論JVM時,我們的意思可能是:

1.      對JVM規範的的比較抽象的說明;

2.      對JVM的具體實現;

3.      在程式運行期間所產生的一個JVM執行個體。

對JVM規範的的抽象說明是一些概念的集合,它們已經在書《The Java Virtual Machine Specification》(《Java虛擬機器規範》)中被詳細地描述了;對JVM的具體實現要麼是軟體,要麼是軟體和硬體的組合,它已經被許多生產廠商所實現,並存在於多種平台之上;運行Java程式的任務由JVM的運行期執行個體單個承擔。在本文中我們所討論的Java虛擬機器(JVM)主要針對第三種情況而言。它可以被看成一個想象中的機器,在實際的電腦上通過軟體類比來實現,有自己想象中的硬體,如處理器、堆棧、寄存器等,還有自己相應的指令系統。

JVM在它的生存周期中有一個明確的任務,那就是運行Java程式,因此當Java程式啟動的時候,就產生JVM的一個執行個體;當程式運行結束的時候,該執行個體也跟著消失了。下面我們從JVM的體繫結構和它的運行過程這兩個方面來對它進行比較深入的研究。

2  Java虛擬機器的體繫結構

剛才已經提到,JVM可以由不同的廠商來實現。由於廠商的不同必然導致JVM在實現上的一些不同,然而JVM還是可以實現跨平台的特性,這就要歸功於設計JVM時的體繫結構了。

我們知道,一個JVM執行個體的行為不光是它自己的事,還涉及到它的子系統、儲存地區、資料類型和指令這些部分,它們描述了JVM的一個抽象的內部體繫結構,其目的不光規定實現JVM時它內部的體繫結構,更重要的是提供了一種方式,用於嚴格定義實現時的外部行為。每個 JVM都有兩種機制,一個是裝載具有合適名稱的類(類或是介面),叫做類裝載子系統;另外的一個負責執行包含在已裝載的類或介面中的指令,叫做運行引擎。每個JVM又包括方法區、堆、Java棧、程式計數器和本地方法棧這五個部分,這幾個部分和類裝載機制與運行引擎機制一起組成的體繫結構圖為:

 

圖3  JVM的體繫結構

JVM的每個執行個體都有一個它自己的方法域和一個堆,運行於JVM內的所有的線程都共用這些地區;當虛擬機器裝載類檔案的時候,它解析其中的位元據所包含的類資訊,並把它們放到方法域中;當程式啟動並執行時候,JVM把程式初始化的所有對象置於堆上;而每個線程建立的時候,都會擁有自己的程式計數器和Java棧,其中程式計數器中的值指向下一條即將被執行的指令,線程的Java棧則儲存為該線程調用Java方法的狀態;本地方法調用的狀態被儲存在本地方法棧,該方法棧依賴於具體的實現。

下面分別對這幾個部分進行說明。

執行引擎處於JVM的核心位置,在Java虛擬機器規範中,它的行為是由指令集所決定的。儘管對於每條指令,規範很詳細地說明了當JVM執行位元組碼遇到指令時,它的實現應該做什麼,但對於怎麼做卻言之甚少。Java虛擬機器支援大約248個位元組碼。每個位元組碼執行一種基本的CPU運算,例如,把一個整數加到寄存器,子程式轉移等。Java指令集相當於Java程式的組合語言。

Java指令集中的指令包含一個單位元組的操作符,用於指定要執行的操作,還有0個或多個運算元,提供操作所需的參數或資料。許多指令沒有運算元,僅由一個單位元組的操作符構成。

虛擬機器的內層迴圈的執行過程如下:
do{
取一個操作符位元組;
根據操作符的值執行一個動作;
}while(程式未結束)

由於指令系統的簡單性,使得虛擬機器執行的過程十分簡單,從而有利於提高執行的效率。指令中運算元的數量和大小是由操作符決定的。如果運算元比一個位元組大,那麼它儲存的順序是高位位元組優先。例如,一個16位的參數存放時佔用兩個位元組,其值為:

第一個位元組*256+第二個位元組位元組碼。

指令流一般只是位元組對齊的。指令tableswitch和lookup是例外,在這兩條指令內部要求強制的4位元組邊界對齊。

對於本地方法介面,實現JVM並不要求一定要有它的支援,甚至可以完全沒有。Sun公司實現Java本地介面(JNI)是出於可移植性的考慮,當然我們也可以設計出其它的本地介面來代替Sun公司的JNI。但是這些設計與實現是比較複雜的事情,需要確保記憶體回收行程不會將那些正在被本地方法調用的對象釋放掉。

Java的堆是一個運行時資料區,類的執行個體(對象)從中分配空間,它的管理是由記憶體回收來負責的:不給程式員顯式釋放對象的能力。Java不規定具體使用的記憶體回收演算法,可以根據系統的需求使用各種各樣的演算法。

Java方法區與傳統語言中的編譯後代碼或是Unix進程中的本文段類似。它儲存方法代碼(編譯後的 java代碼)和符號表。在當前的Java實現中,方法代碼不包括在記憶體回收堆中,但計劃在將來的版本中實現。每個類檔案包含了一個Java類或一個 Java介面的編譯後的代碼。可以說類檔案是Java語言的執行代碼檔案。為了保證類檔案的平台無關性,Java虛擬機器規範中對類檔案的格式也作了詳細的說明。其具體細節請參考Sun公司的Java虛擬機器規範。

Java虛擬機器的寄存器用於儲存機器的運行狀態,與微處理器中的某些專用寄存器類似。Java虛擬機器的寄存器有四種:

1.      pc: Java程式計數器;

2.      optop: 指向運算元棧頂端的指標;

3.      frame: 指向當前執行方法的執行環境的指標;。

4.      vars: 指向當前執行方法的局部變數區第一個變數的指標。

在上述體繫結構圖中,我們所說的是第一種,即程式計數器,每個線程一旦被建立就擁有了自己的程式計數器。當線程執行Java方法的時候,它包含該線程正在被執行的指令的地址。但是若線程執行的是一個本地的方法,那麼程式計數器的值就不會被定義。

Java虛擬機器的棧有三個地區:局部變數區、運行環境區、運算元區。

局部變數區

每個Java方法使用一個固定大小的局部變數集。它們按照與vars寄存器的字位移量來定址。局部變數都是32位的。長整數和雙精確度浮點數佔據了兩個局部變數的空間,卻按照第一個局部變數的索引來定址。(例如,一個具有索引n的局部變數,如果是一個雙精確度浮點數,那麼它實際佔據了索引n和n+1所代表的儲存空間)虛擬機器規範並不要求在局部變數中的64位的值是64位對齊的。虛擬機器提供了把局部變數中的值裝載到運算元棧的指令,也提供了把運算元棧中的值寫入局部變數的指令。

運行環境區

在運行環境中包含的資訊用於動態連結,正常的方法返回以及異常捕捉。

動態連結

運行環境包括對指向當前類和當前方法的解譯器符號表的指標,用於支援方法代碼的動態連結。方法的class 檔案代碼在引用要調用的方法和要訪問的變數時使用符號。動態連結把符號形式的方法調用翻譯成實際方法調用,裝載必要的類以解釋還沒有定義的符號,並把變數訪問翻譯成與這些變數運行時的儲存結構相應的位移地址。動態連結方法和變數使得方法中使用的其它類的變化不會影響到本程式的代碼。

正常的方法返回

如果當前方法正常地結束了,在執行了一條具有正確類型的返回指令時,調用的方法會得到一個傳回值。執行環境在正常返回的情況下用於恢複調用者的寄存器,並把調用者的程式計數器增加一個恰當的數值,以跳過已執行過的方法調用指令,然後在調用者的執行環境中繼續執行下去。

異常捕捉

異常情況在Java中被稱作Error(錯誤)或Exception(異常),是Throwable類的子類,在程式中的原因是:①動態連結錯,如無法找到所需的class檔案。②運行時錯,如對一個null 指標的引用。程式使用了throw語句。

當異常發生時,Java虛擬機器採取如下措施:

·         檢查與當前方法相聯絡的catch子句表。每個catch子句包含其有效指令範圍,能夠處理的異常類型,以及處理異常的代碼塊地址。

·         與異常相匹配的catch子句應該符合下面的條件:造成異常的指令在其指令範圍之內,發生的異常類型是其能處理的異常類型的子類型。如果找到了匹配的catch子句,那麼系統轉移到指定的異常處理塊處執行;如果沒有找到異常處理塊,重複尋找匹配的catch子句的過程,直到當前方法的所有嵌套的catch子句都被檢查過。

·         由於虛擬機器從第一個匹配的catch子句處繼續執行,所以catch子句表中的順序是很重要的。因為Java代碼是結構化的,因此總可以把某個方法的所有的異常處理器都按序排列到一個表中,對任意可能的程式計數器的值,都可以用線性順序找到合適的異常處理塊,以處理在該程式計數器值下發生的異常情況。

·         如果找不到匹配的catch子句,那麼當前方法得到一個"未截獲異常"的結果並返回到當前方法的調用者,好像異常剛剛在其調用者中發生一樣。如果在調用者中仍然沒有找到相應的異常處理塊,那麼這種錯誤將被傳播下去。如果錯誤被傳播到最頂層,那麼系統將調用一個預設的異常處理塊。

運算元棧區

機器指令只從運算元棧中取運算元,對它們進行操作,並把結果返回到棧中。選擇棧結構的原因是:在只有少量寄存器或非通用寄存器的機器(如Intel486)上,也能夠高效地類比虛擬機器的行為。運算元棧是32位的。它用於給方法傳遞參數,並從方法接收結果,也用於支援操作的參數,並儲存操作的結果。例如,iadd指令將兩個整數相加。相加的兩個整數應該是運算元棧頂的兩個字。這兩個字是由先前的指令壓進堆棧的。這兩個整數將從堆棧彈出、相加,並把結果壓回到運算元棧中。

每個未經處理資料類型都有專門的指令對它們進行必須的操作。每個運算元在棧中需要一個儲存位置,除了long 和double型,它們需要兩個位置。運算元只能被適用於其類型的操作符所操作。例如,壓入兩個int類型的數,如果把它們當作是一個long類型的數則是非法的。在Sun的虛擬機器實現中,這個限制由位元組碼驗證器強制實行。但是,有少數操作(操作符dupe和swap),用於對運行時資料區進行操作時是不考慮類型的。

本地方法棧,當一個線程調用本地方法時,它就不再受到虛擬機器關於結構和安全限制方面的約束,它既可以訪問虛擬機器的運行期資料區,也可以使用本地處理器以及任何類型的棧。例如,本地棧是一個C語言的棧,那麼當C程式調用C函數時,函數的參數以某種順序被壓入棧,結果則返回給調用函數。在實現Java虛擬機器時,本地方法介面使用的是C語言的模型棧,那麼它的本地方法棧的調度與使用則完全與C語言的棧相同。

3  Java虛擬機器的運行過程

上面對虛擬機器的各個部分進行了比較詳細的說明,下面通過一個具體的例子來分析它的運行過程。

虛擬機器通過調用某個指定類的方法main啟動,傳遞給main一個字串數組參數,使指定的類被裝載,同時連結該類所使用的其它的類型,並且初始化它們。例如對於程式:

class HelloApp 
{
    public static void main(String[] args) 
    {
        System.out.println("Hello World!"); 
        for (int i = 0; i < args.length; i++ )
        {
            System.out.println(args[i]);
        }
    }
}

 

編譯後在命令列模式下鍵入: java HelloApp run virtual machine

將通過調用HelloApp的方法main來啟動java虛擬機器,傳遞給main一個包含三個字串"run"、"virtual"、"machine"的數組。現在我們略述虛擬機器在執行HelloApp時可能採取的步驟。

開始試圖執行類HelloApp的main方法,發現該類並沒有被裝載,也就是說虛擬機器當前不包含該類的二進位代表,於是虛擬機器使用ClassLoader試圖尋找這樣的二進位代表。如果這個進程失敗,則拋出一個異常。類被裝載後同時在main方法被調用之前,必須對類HelloApp與其它類型進行連結然後初始化。連結包含三個階段:檢驗,準備和解析。檢驗檢查被裝載的主類的符號和語義,準備則建立類或介面的靜態域以及把這些域初始化為標準的預設值,解析負責檢查主類對其它類或介面的符號引用,在這一步它是可選的。類的初始化是對類中聲明的靜態初始化函數和靜態域的初始化構造方法的執行。一個類在初始化之前它的父類必須被初始化。整個過程如下:

 

圖4:虛擬機器的運行過程

4  結束語

本文通過對JVM的體繫結構的深入研究以及一個Java程式執行時虛擬機器的運行過程的詳細分析,意在剖析清楚Java虛擬機器的機

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.