JVM 篇:虛擬機字節碼執行引擎

概述

  Java 虛擬機規範規定了字節碼執行引擎的規範,這個規範形成了所有具體虛擬機實現的統一外觀:輸入字節碼,內部解析,輸出結果。
  執行引擎的運行其實就是棧幀的入棧和出棧的過程。每個棧幀都包括了 局部變量表 (local variable table,也稱本地變量表)操作數棧 (operand stack)棧幀信息 (動態連接,方法返回地址和其他一些額外信息)


棧幀結構

局部變量表

  用於存儲入參和內部定義的局部變量,當代碼被編譯成 Class 文件的時候,方法的 Code 屬性的 max_locals 就已經定義了局部變量表的最大容量。(Code 爲方法的元信息和方法體)
  局部變量以變量槽 (variable slot,簡稱 slot) 爲單位,Java 虛擬機規範中規定一個 slot 爲 32 位,但也有一些虛擬機使用 64 位的 slot。slot 用來存放虛擬機基本類型的數據 (boolean, int, double, reference …)。如果是 double 或是 long 這種 64 位長的數據類型,就使用兩個 slot 來存放,訪問時必須兩個 slot 一起訪問。
  每個棧幀都至少含有一個 slot(用來存放 this)。

操作數棧

  和局部變量表一樣,操作數棧的最大深度在編譯時就已經確定 (由 Code 屬性的 max_stacks 確定)
  操作數棧並不是什麼很高深的東西,它的原理很簡單,接觸過數據結構的人應該都知道,就是一個用來計算加減乘除的數據結構而已 (將表達式轉化爲後綴表達式,讀到數就將數壓到棧中,讀到符號就從棧中去兩個數來計算,再壓入棧中,如 1 + 3 就是 1 3 +,讀到 1 和 3 的時候將它們壓入棧中,讀到 + 的時候將 1 和 3 讀出來相加,再將結果 4 壓入棧中)
  實際的操作數棧比上面講的要複雜點,因爲虛擬機一般會加入一些優化,如大多數虛擬機會將操作數棧數據重疊的地方共享,這樣可以避免數據的複製。

動態連接

  解析階段的時候會將一部分的符號引用轉換爲直接引用,而剩下的一些符號引用需要在運行時才能轉換,這部分就被稱爲動態連接。

方法返回地址

  執行一個方法的時候只有兩種方式可以退出:正常方法出口和異常方法出口。而在方法退出的時候會在棧幀中保存方法返回的地址,方便程序繼續執行。正常方法出口的方法返回地址一般是程序計數器最後記錄的地址;異常方法出口由異常表來決定。


基於棧的字節碼解釋執行引擎

  字節碼執行引擎有解釋執行引擎和編譯執行引擎 (通過 JIT)。現在講的是解釋執行引擎,Java 虛擬機最基本的引擎。
  如下圖可以看到從代碼到執行的過程中在 抽象語法樹 那裏有個分叉,中間那一條則是解釋執行 (Java 等運行在虛擬機上的語言);下面那一條就是編譯執行 (C / C++ 等)
在這裏插入圖片描述
  Java 語言的執行使用的是半獨立的編譯器 (從程序源碼到抽象語法樹,再到指令流的過程,如 javac)解釋器 則在 Java 虛擬機內部。

基於棧的指令集

  基於棧的指令集是相對於基於寄存器的指令集。

  • 基於棧的指令集指令更加緊湊,並且因爲和具體的硬件環境無關所以可以移植。缺點是指令相對來說更多,並且因爲棧是基於內存的所以也導致訪問內存的次數更多,速度較慢。(虛擬機一般通過棧頂緩存將頻繁使用的指令映射到寄存器中來加快速度)

  以上內容爲閱讀 深入理解Java虛擬機(第2版)後的筆記及對 JDK8 的實踐補充。看完這本書後最大的感覺就是,,,再看一遍,很多原來理解不了的知識點就可以看懂了,因爲很多內容是前後呼應的。有興趣的可以去閱讀這本書,強推。

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