前言
眾所周知,計算機只識別0和1宙帝,程序員寫的c或c++程序最終都要經(jīng)過編譯丧凤、鏈接等步驟,將代碼轉(zhuǎn)換成0或1的二進制格式才能被計算機執(zhí)行步脓。由于依賴平臺愿待,導致c語音中基礎數(shù)據(jù)類型在各平臺上所占的字節(jié)數(shù)都不相同,其它細節(jié)也有不同靴患。
Java不一樣仍侥,Java在誕生之初就宣稱“write once,run anywhere”鸳君,它選擇了與操作系統(tǒng)和機器指令集無關的农渊、平臺中立的格式作為程序編譯后的存儲格式。
實現(xiàn)語言無關性的基礎就是虛擬機和字節(jié)碼存儲格式相嵌,使用Java編譯器可以把Java代碼編譯為存儲字節(jié)碼的Class文件腿时,使用JRuby等的編譯器一樣可以把代碼文件編譯成Class文件况脆,虛擬機并不關心Class文件的來源饭宾,只要它符合Class文件就有的結構就可以在Java虛擬機中運行。
Class類文件的結構
解析學習Class類文件的結構格了,是了解虛擬機的重要基礎之一看铆,有助于學習虛擬機如何加載并執(zhí)行class文件。
Class文件是一組以8位字節(jié)為基礎單位的二進制流盛末,各個數(shù)據(jù)項目嚴格按照順序緊湊地排列在Class文件之中弹惦,中間沒有添加任何分隔符。
Class文件采用一種類似C語言結構體的偽結構來存儲悄但,這種偽結構中只有兩種數(shù)據(jù)類型:無符號數(shù)和表棠隐。
無符號數(shù)基于基本的數(shù)據(jù)類型,以u1檐嚣,u2助泽,u4,u8來分別代表1個字節(jié)、2個字節(jié)嗡贺、4個字節(jié)隐解、8個字節(jié)的無符號數(shù),無符號數(shù)可以用來描述數(shù)字诫睬、索引引用煞茫、數(shù)量值或按照UTF-8編碼構成的字符串值。
無論是無符號數(shù)還是表摄凡,當需要描述同一類型但數(shù)量不定的多個數(shù)據(jù)時续徽,經(jīng)常會使用一個前置的容量計數(shù)器加若干個連續(xù)的數(shù)據(jù)項的形式,這時候稱這一系列連續(xù)的某一類型的數(shù)據(jù)為某一類型的集合亲澡。
注意:Class文件不像xml炸宵,它沒有分隔符,而且它的順序都是嚴格定義的谷扣。
Java代碼中包含成員變量土全,方法,變量還定義了訪問權限会涎,方法還有參數(shù)裹匙、返回值,方法中的邏輯(比如說int型數(shù)據(jù)相加)是否會被存儲成一個個指令末秃,Class文件會如何描述這些東西呢概页?
示例代碼
以如下代碼為示例,查看編譯后的Class文件练慕。
package org.fenixsoft.clazz;
public class TestClass {
private int m;
public int inc(){
return m + 1;
}
}
Class文件是字節(jié)碼文件惰匙,需要使用16進制編輯器查看,本文中選用 WinHex铃将,打開Class文件后整體如下:
另外Class文件可以使用javap工具查看项鬼,指令如下:javap -verbose class文件路徑
魔數(shù)與Class文件版本
每個Class文件的頭4個字節(jié)稱為魔數(shù),它的唯一作用是用于確定這個文件是否為Class文件劲阎。查看上單節(jié)的Class文件格式圖绘盟,魔數(shù)正好也是由一個u4類型表示。
Class文件的魔數(shù)為 cafebabe(咖啡寶貝悯仙?)龄毡。魔數(shù)之后即是次版本號和主版本號,二者均由一個u2表示锡垄。
本例中次版本號為0沦零,主版本號為51(33是16進制數(shù),換成10進制就是51)货岭,即為Java 1.7路操。注意高版本的JDK能向下兼容序攘,但無法運行以后版本的Class文件,那么本例中的Class文件只能被JDK 1.7及以上運行寻拂。
常量池
主版本號后邊就是常量池了程奠,常量池中的所有項目類型為:
使用javap -verbose 查看Class文件,查看常量池部分
常量池中存放兩大類常量:字面量和符號引用祭钉。字面量瞄沙,如字符串、被聲明為final的常量值等慌核。而符號引用(符號引用屬于編譯原理方面的概念)包括了下面三類常量:
- 類和接口的全限定名
- 字段的名稱和描述符
- 方法的名稱和描述符
和Java代碼中的常量不一致距境,常量池中并不是只會存儲final類型的數(shù)據(jù),不是常量也會被存儲垮卓,比如示例代碼中的m垫桂,另外符號引用也并不是代碼中所說的引用類型,除了類和接口粟按,它還包含字段和方法诬滩。
虛擬機在加載Class文件時進行動態(tài)連接,也就是說Class文件并不會保存各個方法和字段的最終內(nèi)存布局信息灭将,當虛擬機運行時疼鸟,需要從常量池中獲取對應的符號引用,在運行時解析并翻譯到具體的內(nèi)存地址當中庙曙。
常量池是最繁瑣的數(shù)據(jù)了空镜,因為常量池中所包含的11種類型都有自己的結構。一般是由一位u1指定類型捌朴,再加上數(shù)據(jù)長度(一般是u2)吴攒,再加上數(shù)據(jù)具體值。