天天看點

Java虛拟機概念及體系結構詳述(二)

虛拟機的内層循環的執行過程如下:

  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虛拟機規範。