Java虛擬機(JVM)是可運行Java代碼的假想計算機
只要根據JVM規格描述將解釋器移植到特定的計算機上
就能保證經過編譯的任何Java代碼能夠在該系統上運行
本文首先簡要介紹從Java文件的編譯到最終執行的過程
隨後對JVM規格描述作一說明
一Java源文件的編譯下載解釋和執行 Java應用程序的開發周期包括編譯
下載
解釋和執行幾個部分
Java編譯程序將Java源程序翻譯為JVM可執行代碼?字節碼
這一編譯過程同C/C++的編譯有些不同
當C編譯器編譯生成一個對象的代碼時
該代碼是為在某一特定硬件平台運行而產生的
因此
在編譯過程中
編譯程序通過查表將所有對符號的引用轉換為特定的內存偏移量
以保證程序運行
Java編譯器卻不將對變量和方法的引用編譯為數值引用
也不確定程序執行過程中的內存布局
而是將這些符號引用信息保留在字節碼中
由解釋器在運行過程中創立內存布局
然後再通過查表來確定一個方法所在的地址
這樣就有效的保證了Java的可移植性和安全性
運行JVM字節碼的工作是由解釋器來完成的
解釋執行過程分三部進行
代碼的裝入
代碼的校驗和代碼的執行
裝入代碼的工作由
類裝載器
(class loader)完成
類裝載器負責裝入運行一個程序需要的所有代碼
這也包括程序代碼中的類所繼承的類和被其調用的類
當類裝載器裝入一個類時
該類被放在自己的名字空間中
除了通過符號引用自己名字空間以外的類
類之間沒有其他辦法可以影響其他類
在本台計算機上的所有類都在同一地址空間內
而所有從外部引進的類
都有一個自己獨立的名字空間
這使得本地類通過共享相同的名字空間獲得較高的運行效率
同時又保證它們與從外部引進的類不會相互影響
當裝入了運行程序需要的所有類後
解釋器便可確定整個可執行程序的內存布局
解釋器為符號引用同特定的地址空間建立對應關系及查詢表
通過在這一階段確定代碼的內存布局
Java很好地解決了由超類改變而使子類崩潰的問題
同時也防止了代碼對地址的非法訪問
隨後
被裝入的代碼由字節碼校驗器進行檢查
校驗器可發現操作數棧溢出
非法數據類型轉化等多種錯誤
通過校驗後
代碼便開始執行了
Java字節碼的執行有兩種方式
即時編譯方式
解釋器先將字節碼編譯成機器碼
然後再執行該機器碼
解釋執行方式
解釋器通過每次解釋並執行一小段代碼來完成Java字節碼程 序的所有操作
通常采用的是第二種方法
由於JVM規格描述具有足夠的靈活性
這使得將字節碼翻譯為機器代碼的工作
具有較高的效率
對於那些對運行速度要求較高的應用程序
解釋器可將Java字節碼即時編譯為機器碼
從而很好地保證了Java代碼的可移植性和高性能
二JVM規格描述 JVM的設計目標是提供一個基於抽象規格描述的計算機模型
為解釋程序開發人員提很好的靈活性
同時也確保Java代碼可在符合該規范的任何系統上運行
JVM對其實現的某些方面給出了具體的定義
特別是對Java可執行代碼
即字節碼(Bytecode)的格式給出了明確的規格
這一規格包括操作碼和操作數的語法和數值
標識符的數值表示方式
以及Java類文件中的Java對象
常量緩沖池在JVM的存儲映象
這些定義為JVM解釋器開發人員提供了所需的信息和開發環境
Java的設計者希望給開發人員以隨心所欲使用Java的自由
JVM定義了控制Java代碼解釋執行和具體實現的五種規格
它們是
JVM指令系統
JVM寄存器
JVM棧結構
JVM碎片回收堆
JVM存儲區
JVM指令系統
JVM指令系統同其他計算機的指令系統極其相似
Java指令也是由 操作碼和操作數兩部分組成
操作碼為
位二進制數
操作數進緊隨在操作碼的後面
其長度根據需要而不同
操作碼用於指定一條指令操作的性質(在這裡我們采用匯編符號的形式進行說明)
如iload表示從存儲器中裝入一個整數
anewarray表示為一個新數組分配空間
iand表示兩個整數的
與
ret用於流程控制
表示從對某一方法的調用中返回
當長度大於
位時
操作數被分為兩個以上字節存放
JVM采用了
big endian
的編碼方式來處理這種情況
即高位bits存放在低字節中
這同 Motorola及其他的RISC CPU采用的編碼方式是一致的
而與Intel采用的
little endian
的編碼方式即低位bits存放在低位字節的方法不同
Java指令系統是以Java語言的實現為目的設計的
其中包含了用於調用方法和監視多先程系統的指令
Java的
位操作碼的長度使得JVM最多有
種指令
目前已使用了
多種操作碼
JVM指令系統
所有的CPU均包含用於保存系統狀態和處理器所需信息的寄存器組
如果虛擬機定義較多的寄存器
便可以從中得到更多的信息而不必對棧或內存進行訪問
這有利於提高運行速度
然而
如果虛擬機中的寄存器比實際CPU的寄存器多
在實現虛擬機時就會占用處理器大量的時間來用常規存儲器模擬寄存器
這反而會降低虛擬機的效率
針對這種情況
JVM只設置了
個最為常用的寄存器
它們是
pc程序計數器
optop操作數棧頂指針
frame當前執行環境指針
vars指向當前執行環境中第一個局部變量的指針
所有寄存器均為
位
pc用於記錄程序的執行
optop
frame和vars用於記錄指向Java棧區的指針
JVM棧結構
作為基於棧結構的計算機
Java棧是JVM存儲信息的主要方法
當JVM得到一個Java字節碼應用程序後
便為該代碼中一個類的每一個方法創建一個棧框架
以保存該方法的狀態信息
每個棧框架包括以下三類信息
局部變量
執行環境
操作數棧
局部變量用於存儲一個類的方法中所用到的局部變量
vars寄存器指向該變量表中的第一個局部變量
執行環境用於保存解釋器對Java字節碼進行解釋過程中所需的信息
它們是
上次調用的方法
局部變量指針和操作數棧的棧頂和棧底指針
執行環境是一個執行一個方法的控制中心
例如
如果解釋器要執行iadd(整數加法)
首先要從frame寄存器中找到當前執行環境
而後便從執行環境中找到操作數棧
從棧頂彈出兩個整數進行加法運算
最後將結果壓入棧頂
操作數棧用於存儲運算所需操作數及運算的結果
JVM碎片回收堆
Java類的實例所需的存儲空間是在堆上分配的
解釋器具體承擔為類實例分配空間的工作
解釋器在為一個實例分配完存儲空間後
便開始記錄對該實例所占用的內存區域的使用
一旦對象使用完畢
便將其回收到堆中
在Java語言中
除了new語句外沒有其他方法為一對象申請和釋放內存
對內存進行釋放和回收的工作是由Java運行系統承擔的
這允許Java運行系統的設計者自己決定碎片回收的方法
在SUN公司開發的Java解釋器和Hot Java環境中
碎片回收用後台線程的方式來執行
這不但為運行系統提供了良好的性能
而且使程序設計人員擺脫了自己控制內存使用的風險
JVM存儲區
JVM有兩類存儲區
常量緩沖池和方法區
常量緩沖池用於存儲類名稱
方法和字段名稱以及串常量
方法區則用於存儲Java方法的字節碼
對於這兩種存儲區域具體實現方式在JVM規格中沒有明確規定
這使得Java應用程序的存儲布局必須在運行過程中確定
依賴於具體平台的實現方式
JVM是為Java字節碼定義的一種獨立於具體平台的規格描述
是Java平台獨立性的基礎
目前的JVM還存在一些限制和不足
有待於進一步的完善
但無論如何
JVM的思想是成功的
對比分析
如果把Java原程序想象成我們的C++原程序
Java原程序編譯後生成的字節碼就相當於C++原程序編譯後的
x
的機器碼(二進制程序文件)
JVM虛擬機相當於
x
計算機系統
Java解釋器相當於
x
CPU
在
x
CPU上運行的是機器碼
在Java解釋器上運行的是Java字節碼
Java解釋器相當於運行Java字節碼的
CPU
但該
CPU
不是通過硬件實現的
而是用軟件實現的
Java解釋器實際上就是特定的平台下的一個應用程序
只要實現了特定平台下的解釋器程序
Java字節碼就能通過解釋器程序在該平台下運行
這是Java跨平台的根本
當前
並不是在所有的平台下都有相應Java解釋器程序
這也是Java並不能在所有的平台下都能運行的原因
它只能在已實現了Java解釋器程序的平台下運行
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26222.html