深入理解Java虛擬機_1_JVM的由來

一、爲什麼要使用JVM   

    所有的程序運行所需要的底層機器指令都只是有限的若干條,從大型的UNIX機器到桌面個人計算機,都是基於那些設計精良而優美的指令集。但是這些指令集之間互不相容,這就使得程序的移植變得非常困難,所需時間甚至超過了重新編寫一遍的時間。於是虛擬機的概念出現了。

   Java虛擬機(JVM)在多個平臺上實現統一語言,.NET的虛擬機(目前)在單一平臺上實現多種語言。它們都是抽象的計算機,或者可以看做一個操作系統。儘管它們都有自己的指令集,自己的內存體系,但它們卻往往比實際的硬件機器簡單明瞭。


二、Java虛擬機是什麼

  Java虛擬機之所以被稱爲是"虛擬"的,就是因爲它僅僅是由一個規範來定義的抽象計算機。因此,要運行一個Java程序,首先需要一個符合該規範的具體實現。

   當你說"Java虛擬機"時,有可能指的是如下三種不同的東西:

  • 抽象規範
  • 一個具體的實現
  • 一個運行中的虛擬機實例


三、JVM有什麼作用

   Java虛擬機負責Java程序設計語言的內存安全、平臺無關和安全特性。


四、Java體系結構

  Java體系結構包括四個獨立但相關的技術:

  • Java程序設計語言
  • Javaclass文件格式
  • Java應用編程接口(API)
  • Java虛擬機


五、 Java虛擬機的聲明週期

    一個運行時的Java虛擬機的作用就是:負責一個Java程序。當啓動一個Java程序時,一個虛擬機實例也誕生了。當該程序關閉退出,這個虛擬機實例也就隨之小王。如果在同一臺計算機上同時運行三個Java程序,則會得到三個Java虛擬機實例。每個java程序都運行於它自己的Java虛擬機實例中。

    Java程序初始類中的main()方法,將作爲該程序初始線程的起點,任何其他線程都是由這個初始線程啓動的。

   Java虛擬機中有兩種線程:守護線程和非守護線程。守護線程是由虛擬機自己使用的,比如執行垃圾收集任務的線程。不過,Java程序也可以把它創建的任何線程標記爲守護線程。

   Java程序中的初始線程(開始於main()方法的那個線程),是非守護線程。

  只要還有任何非守護線程在運行,那麼這個Java程序也在繼續運行(虛擬機仍然存活)。當該程序中所有的非守護線程都終止時,虛擬機實例將自動退出。


六、Java虛擬機支持的數據類型

    Java虛擬機支持Java語言的基本數據類型如下:

1.  數值類型

  • byte:       //1字節有符號整數的補碼
  • short:      //2字節有符號整數的補碼
  • int:          //4字節有符號整數的補碼
  • long:      //8字節有符號整數的補碼
  • char:     //2字節無符號Unicode字符
  • float:    //4字節IEEE754單精度浮點數
  • double:  //8字節IEEE754雙精度浮點數

    幾乎所有的Java類型檢查都是在編譯時完成的。

    上面列出的原始數據類型的數據在Java執行時不需要用硬件標記。操作這些原始數據類型數據的字節碼(指令)本身就已經指出了操作數的數據類型,例如iadd、ladd、fadd和dadd指令都是把兩個數相加,其操作數類型別是int、long、float和double。虛擬機使用IEEE754格式的浮點數。不支持IEEE格式的較舊的計算機,在運行Java數值計算程序時,可能會非常慢。

2.  其它基本數據類型包括:

  • boolean
  • returnAddress    //4字節,用於jsr/ret/jsr-w/ret-w指令,同一方法中某操作碼的地址

     虛擬機沒有給boolean(布爾)類型設置單獨的指令,指令集對boolean只是很有限的支持。當編譯器把Java源碼編譯爲字節碼時,boolean型的數據是由integer指令包括integer返回來處理的;boolean型的數組則是用byte數組來處理的。在Java虛擬機中,false是由整數零來表示的,所有非零整數都表示true。

     Java虛擬機還有一個只在內部使用的基本類型: returnAddress。Java程序員不能使用這個類型。這個基本類型被用來實現Java程序中的finnally字句。

3. 引用類型

  • reference(引用)      //對一個Javaobject(對象)的4字節引用

     ----類類型

     ----接口類型

     ----數組類型

     注:Java數組被當作object處理。

     虛擬機的規範對於object內部的結構沒有任何特殊的要求。在Sun公司的實現中,對object的引用是一個句柄,其中包含一對指針:一個指針指向該object的方法表,另一個指向該object的數據。用Java虛擬機的字節碼錶示的程序應該遵守類型規定。Java虛擬機的實現應拒絕執行違反了類型規定的字節碼程序。

     Java虛擬機由於字節碼定義的限制似乎只能運行於32位地址空間的機器上。但是可以創建一個Java虛擬機,它自動地把字節碼轉換成64位的形式。從Java虛擬機支持的數據類型可以看出,Java對數據類型的內部格式進行了嚴格規定,這樣使得各種Java虛擬機的實現對數據的解釋是相同的,從而保證了Java的與平臺無關性和可移植性。


    Java虛擬機規範定義了每一種數據類型的取值範圍,但是卻沒有定義它們的位寬。存儲這些類型的的佔位寬度,是由具體的虛擬機實現的設計者決定的。


七、 Java虛擬機體系結構

Java虛擬機由五個部分組成:一組指令集、一組寄存器、一個棧、一個無用單元收集堆(Garbage-collected-heap)、一個方法區域。這五部分是Java虛擬機的邏輯成份,不依賴任何實現技術或組織方式,但它們的功能必須在真實機器上以某種方式實現。
1.Java指令集
Java虛擬機支持大約248個字節碼。每個字節碼執行一種基本的CPU運算,例如,把一個整數加到寄存器,子程序轉移等。Java指令集相當於Java程序的彙編語言。
Java指令集中的指令包含一個單字節的操作符,用於指定要執行的操作,還有0個或多個操作數,提供操作所需的參數或數據。許多指令沒有操作數,僅由一個單字節的操作符構成。
虛擬機的內層循環的執行過程如下:
do{
取一個操作符字節;
根據操作符的值執行一個動作;
}while(程序未結束)
由於指令系統的簡單性,使得虛擬機執行的過程十分簡單,從而有利於提高執行的效率。指令中操作數的數量和大小是由操作符決定的。如果操作數比一個字節大,那麼它存儲的順序是高位字節優先。例如,一個16位的參數存放時佔用兩個字節,其值爲:
第一個字節*256+第二個字節字節碼指令流一般只是字節對齊的。指令tableswitch和lookup是例外,在這兩條指令內部要求強制的4字節邊界對齊。
2.寄存器
Java虛擬機的寄存器用於保存機器的運行狀態,與微處理器中的某些專用寄存器類似。
Java虛擬機的寄存器有四種:
◆pc:Java程序計數器。
◆optop:指向操作數棧頂端的指針。
◆frame:指向當前執行方法的執行環境的指針。
◆vars:指向當前執行方法的局部變量區第一個變量的指針。
Java虛擬機
Java虛擬機是棧式的,它不定義或使用寄存器來傳遞或接受參數,其目的是爲了保證指令集的簡潔性和實現時的高效性(特別是對於寄存器數目不多的處理器)。
所有寄存器都是32位的。
3.棧
Java虛擬機的棧有三個區域:局部變量區、運行環境區、操作數區。
(1)局部變量區 每個Java方法使用一個固定大小的局部變量集。它們按照與vars寄存器的字偏移量來尋址。局部變量都是32位的。長整數和雙精度浮點數佔據了兩個局部變量的空間,卻按照第一個局部變量的索引來尋址。(例如,一個具有索引n的局部變量,如果是一個雙精度浮點數,那麼它實際佔據了索引n和n+1所代表的存儲空間。)虛擬機規範並不要求在局部變量中的64位的值是64位對齊的。虛擬機提供了把局部變量中的值裝載到操作數棧的指令,也提供了把操作數棧中的值寫入局部變量的指令。
(2)運行環境區 在運行環境中包含的信息用於動態鏈接,正常的方法返回以及異常傳播。
◆動態鏈接
運行環境包括對指向當前類和當前方法的解釋器符號表的指針,用於支持方法代碼的動態鏈接。方法的class文件代碼在引用要調用的方法和要訪問的變量時使用符號。動態鏈接把符號形式的方法調用翻譯成實際方法調用,裝載必要的類以解釋還沒有定義的符號,並把變量訪問翻譯成與這些變量運行時的存儲結構相應的偏移地址。動態鏈接方法和變量使得方法中使用的其它類的變化不會影響到本程序的代碼。
◆正常的方法返回
如果當前方法正常地結束了,在執行了一條具有正確類型的返回指令時,調用的方法會得到一個返回值。執行環境在正常返回的情況下用於恢復調用者的寄存器,並把調用者的程序計數器增加一個恰當的數值,以跳過已執行過的方法調用指令,然後在調用者的執行環境中繼續執行下去。
◆異常和錯誤傳播
異常情況在Java中被稱作Error(錯誤)或Exception(異常),是Throwable類的子類,在程序中的原因是:①動態鏈接錯,如無法找到所需的class文件。②運行時錯,如對一個空指針的引用
·程序使用了throw語句。
當異常發生時,Java虛擬機採取如下措施:
·檢查與當前方法相聯繫的catch子句表。每個catch子句包含其有效指令範圍,能夠處理的異常類型,以及處理異常的代碼塊地址。
·與異常相匹配的catch子句應該符合下面的條件:造成異常的指令在其指令範圍之內,發生的異常類型是其能處理的異常類型的子類型。如果找到了匹配的catch子句,那麼系統轉移到指定的異常處理塊處執行;如果沒有找到異常處理塊,重複尋找匹配的catch子句的過程,直到當前方法的所有嵌套的catch子句都被檢查過。
·由於虛擬機從第一個匹配的catch子句處繼續執行,所以catch子句表中的順序是很重要的。因爲Java代碼是結構化的,因此總可以把某個方法的所有的異常處理器都按序排列到一個表中,對任意可能的程序計數器的值,都可以用線性的順序找到合適的異常處理塊,以處理在該程序計數器值下發生的異常情況。
·如果找不到匹配的catch子句,那麼當前方法得到一個"未截獲異常"的結果並返回到當前方法的調用者,好像異常剛剛在其調用者中發生一樣。如果在調用者中仍然沒有找到相應的異常處理塊,那麼這種錯誤傳播將被繼續下去。如果錯誤被傳播到最頂層,那麼系統將調用一個缺省的異常處理塊。
(3)操作數棧區 機器指令只從操作數棧中取操作數,對它們進行操作,並把結果返回到棧中。選擇棧結構的原因是:在只有少量寄存器或非通用寄存器的機器(如Intel486)上,也能夠高效地模擬虛擬機的行爲。操作數棧是32位的。它用於給方法傳遞參數,並從方法接收結果,也用於支持操作的參數,並保存操作的結果。例如,iadd指令將兩個整數相加。相加的兩個整數應該是操作數棧頂的兩個字。這兩個字是由先前的指令壓進堆棧的。這兩個整數將從堆棧彈出、相加,並把結果壓回到操作數棧中。
每個原始數據類型都有專門的指令對它們進行必須的操作。每個操作數在棧中需要一個存儲位置,除了long和double型,它們需要兩個位置。操作數只能被適用於其類型的操作符所操作。例如,壓入兩個int類型的數,如果把它們當作是一個long類型的數則是非法的。在Sun的虛擬機實現中,這個限制由字節碼驗證器強制實行。但是,有少數操作(操作符dupe和swap),用於對運行時數據區進行操作時是不考慮類型的。
4.無用單元收集堆
Java的堆是一個運行時數據區,類的實例(對象)從中分配空間。Java語言具有無用單元收集能力:它不給程序員顯式釋放對象的能力。Java不規定具體使用的無用單元收集算法,可以根據系統的需求使用各種各樣的算法。

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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章