資料格式
在說具體的內容之前,我們需要先理解一個概念,資料格式。
所謂資料格式,就是資料按照約定好的格式寫。
比如:
位元組碼
位元組碼的資料格式就是嚴格規定好,前0-3位元組 是魔數,4-5位元組 次版本號,6-7版本號,8-9常量池數量等。
為什麼0-3一定是魔術,4-7是版本號,因為這就是約定好的,你按照這個格式寫,我按照這個格式讀。
JSON
{} 代表物件,[]代表陣列,為什麼,因為我們就這麼約定。
開發
我們開發中可能約定, I 開頭代表介面, impl結尾 代表實現類,等等。都是一種約定好的規則。
符號引用和直接引用
- 符號引用:字串,能根據這個字串定位到指定的資料,比如java/lang/StringBuilder
- 直接引用:記憶體地址
這裡說一下我的理解
首先:JVM 和 class 都有屬於自己的資料結構
其次:class檔案在被載入到jvm記憶體中時候,JVM根據class檔案的資料結構規則,
拆分讀取class檔案,然後把對應的資料放入JVM中
複製程式碼
參考:
- 周志明老師--《深入理解Java虛擬機器》,P215,倒數第六行
載入階段完成後,虛擬機器外部的二進位制位元組流就按照虛擬機器所需的格式儲存在方法區之中。
- 知乎 R大的文章
理解:
class位元組碼的資料結構從前至後,包括 魔/版本/常量池/訪問標誌/類索引/父類索引/介面索引/欄位表/方法表/屬性表
而常量池包括:
-
字面量
-
符號引用
- 類和介面的全限定名
- 欄位名稱和描述符
- 方法名稱和描述符
類載入之後,常量池的內容會進入執行時常量池,這時候裡面的資料也許還保持著符號引用。
(因為解析的時機由JVM自己設定)
如果在虛擬機器棧的 棧幀中,我準備呼叫 main() 函式,那麼會通過棧幀中持有的動態連線,找到執行時常量池,
然後找到main函式的常量 比如 #2 ,如果這個常量沒有被解析過,那麼就通過這個常量進行解析過程,
其中包括,通過常量 找到 類名 和 nameAndType,通過 nameAndType 找到方法名和返回值。
這時候 我手裡有 類名/方法名/方法返回值,下一步,我通過類名和方法名,通過JVM記錄的方法列表,找到對應的方法體。
而這個方法體實際上是一段記憶體地址,那麼這時候我就把這段記憶體地址複製給 #2,並且給 #2設定一個已經解析的 flag。
這樣就完成了 符號引用到直接引用的過程。
複製程式碼
說回虛擬機器載入
虛擬機器載入經歷 載入--驗證--準備--解析--初始化--使用--解除安裝,《深入理解Java虛擬機器》-周志明,書中說,載入過程中也可以驗證,我覺得沒問題,比如,我讀取了class的前四個欄位,載入進來了,然後就直接驗證,看看魔數對不對,不對我就不再載入。這個很OK。
比如魔數對了,那麼我再載入四個位元組,然後驗證,看看版本號能不能被JVM解釋,不能就報錯,也很OK。