JVM字節碼執行引擎和動態綁定原理

1.執行引擎

  • 全部Java虛擬機的執行引擎都是一致的: 輸入的是字節碼文件,處理過程就是解析過程,最後輸出執行結果。
  • 在整個過程不一樣的數據在不一樣的結構中進行處理。

2.棧幀

  • jvm進行方法調用和方法執行的數據結構,是虛擬機棧的元素。
  • (圖片來自網絡)
  1. 棧幀包括:局部變量表、操做數棧、動態連接、方法返回地址等
  2. 編譯期就肯定了須要多大的局部變量表,多深的操做數棧,這些信息全在字節碼中。
  3. 只有位於棧頂的棧幀纔有效,稱爲當前棧幀,所對應的方法就是當前正在執行的方法。

2.1 局部變量表

  • 存放方法參數和方法內部定義的局部變量。
  1. 容量以變量槽slot爲單位,slot內存大小隨着需求而變化而且不固定。
  2. 方法執行時jvm使用局部變量表完成參數值到參數列表的傳遞過程。
  3. slot能夠被其餘變量複用。
  4. 局部變量不存在準備階段,因此不會賦予系統初始值,若是不初始化那麼他就不能使用。
  5. jvm經過索引定位的方式定位slot,從0到最大slot數量。0位索引slot是用來存放方法所屬對象實例的引用(this),以後按參數列表分配slot,再後按照局部變量順序分配slot。

2.2操做數棧

  • 方法剛剛開始執行時爲空,在執行過程當中會有各類字節碼指令往操做數棧中寫入和提取內容,也就是入棧/出棧操做。
  • 概念上兩個棧幀相互獨立,但大多數虛擬機會存在一些優化,讓下面棧幀的操做數棧部分區域和上面棧幀的部分局部變量表重合,成爲共享區域。

2.3動態連接

  • 保存 指向運行時常量池中 該棧幀所屬方法的 引用。
  • 做用就是 運行期間將符號引用轉化爲直接引用。

2.4方法返回地址

  • 保留一些退出方法時 上層方法執行狀態 的信息

3.方法的調用,動態綁定過程。

  • 編譯過程不包括傳統的鏈接,class文件中只是存符號引用,而不是直接引用。
  • 方法調用就是肯定該方法是哪一個類的哪一個方法換個說法就是綁定,這不一樣於方法執行。
  1. 解析

    • 符號引用轉化爲直接引用,由於編譯器並不知道分配內存後某個變量具體內存位置,因此編譯時使用一個符號代替,分配內存後該變量所在的內存位置就等於這個符號,解析階段找到這個符號對應的內存位置而後指向她,這就是符號引用轉化爲直接引用。
    1. 類加載的解析階段會把 部分符號引用轉化爲直接引用,但這種解析的前提條件就是:方法在程序執行以前就有一個就能夠肯定的版本,而且在運行期間不可變。
    2. 編譯期可知運行期不可變這種方法有static和private方法,想一想也是,這兩種方法只有定義他們的類才能調用,他們一出生就肯定了惟一的主人。他們不可能被繼承或者重寫其餘版本。
    3. 實例構造器,父類方法這2中也能肯定惟一的版本,因此這些方法也是在類加載階段解析爲直接引用。
    4. 靜態方法,私有方法,實例構造器,父類方法這些方法可肯定惟一版本因此統稱爲 非虛方法,final方法也是非虛方法。
  2. 分派調用

    • 分派調用就是多態特徵的體現,在重載重寫的方法中正確肯定版本。
    1. 靜態類型和動態類型
      • Human man =  new  Man();
      • Human 就是變量man 的靜態類型, Man 就是man 的實際類型。
      • 靜態類型在編譯期可知,動態類型在運行期才能肯定
    2. 靜態分派:根據參數的靜態類型肯定該方法使用的版本
      • 方法被重載了好幾個版本,肯定要使用哪個版本就是經過當前調用方法傳入參數的靜態類型肯定版本。
      • 靜態分派會找到一個和參數靜態類型最適合的版本,如 int 類型傳入多個沒有int的重載版本中還會找到float版本。
      • 靜態分派在編譯期肯定調用哪個版本,並把符號引用記錄下來。
      • 注意靜態分派只肯定了方法的版本,若是一個類沒有繼承其餘類當前方法也不是覆寫了父類方法,那麼他就只有這一個版本可用,若是重寫了父類方法,那麼就由動態分派來肯定使用哪一個重寫方法。
    3. 動態分派:運行期間根據調用方法的對象的實際類型肯定使用哪一個類的方法,也就是肯定使用哪一個重寫方法。編譯期間沒法肯定實際類型
      1. 找到調用當前方法的對象的實際類型
      2. 在實際類型中由靜態分派肯定方法版本。
      3. 若是找到對應版本則檢查訪問權限若是經過則返回此方法的引用。
      4. 若是找不到則從下往上在父類中找,若是找不到則拋出異常。
    4. 單分派多分派
      • 方法的接收者和參數統稱爲綜量
      • 靜態分派是根據方法參數肯定的,方法參數能夠有多個因此是多個宗量,屬於多分派。
      • 動態分派是根據一個實際類型肯定的因此屬於單宗量,因此是單分派。
      • Java 目前是一門靜態多分派,動態單分派的語言。
    5. 靜態分派和動態分派的區別
      1. 靜態分派經過參數類型查找重載版本,而且是參數的靜態類型。
      2. 動態分派經過調用方法的對象查找重寫版本,而且是對象的實際類型。

 4. 字段的調用和方法的調用不一樣

  1.  當一個引用變量它的編譯時的類型和運行是的類型不同時:
    • 經過引用調用字段則調用的是 編譯時類型(靜態類型 )對象的字段,也就是父類的字段。
    • 經過引用調用方法則是經過動態分派調用實際類型的方法,若是沒有就去父類中查找。
相關文章
相關標籤/搜索