天天看點

(六)-class檔案結構1 什麼是JVM的“無關性”?2 縱觀Class檔案結構

1 什麼是JVM的“無關性”?

Java具有平台無關性,也就是任何作業系統都能運作Java代碼.之是以能實作這一點,是因為Java運作在虛拟機之上,不同的作業系統都擁有各自的Java虛拟機,是以Java能實作"一次編寫,處處運作".

而JVM不僅具有平台無關性,還具有語言無關性.

  • 平台無關性是指不同作業系統都有各自的JVM
  • 語言無關性是指Java虛拟機能運作除Java以外的代碼!

這聽起來非常驚人,但JVM對能運作的語言是有嚴格要求的.首先來了解下Java代碼的運作過程.

Java源代碼首先需要使用Javac編譯器編譯成class檔案,然後啟動JVM執行class檔案,進而程式開始運作.

也就是JVM隻認識class檔案,它并不管何種語言生成了class檔案,隻要class檔案符合JVM的規範就能運作.

是以目前已經有Scala、JRuby、Jython等語言能夠在JVM上運作.它們有各自的文法規則,不過它們的編譯器都能将各自的源碼編譯成符合JVM規範的class檔案,進而能夠借助JVM運作它們.

(六)-class檔案結構1 什麼是JVM的“無關性”?2 縱觀Class檔案結構

這裡寫圖檔描述

2 縱觀Class檔案結構

class檔案是一組以8位位元組為基礎機關的二進制流,它的内容具有嚴格的規範,檔案中沒有任何分隔符,全是連續的0/1.

class檔案中的所有内容被分為兩種類型:無符号數 和 表。

  • 無符号數

    基本的資料類型,以u1、u2、u4、u8,分别代表1位元組、2位元組、4位元組、8位元組的無符号數.

  • class檔案中所有資料(即無符号數)要麼單獨存在,要麼由多個無符号數組成二維表.即class檔案中的資料要麼是單個值,要麼是二維表.

2.1 魔數(Magic Number)

class檔案的頭4個位元組稱為魔數,唯一作用是确定這個檔案是否為一個能被JVM接受的Class檔案.

作用就相當于檔案字尾名,隻不過字尾名容易被修改,不安全.

是用16進制表示的"CAFEBABE".

2.2 版本資訊

緊接着魔數的4個位元組是版本号.它表示本class中使用的是哪個版本的JDK.

在高版本的JVM上能夠運作低版本的class檔案,但在低版本的JVM上無法運作高版本的class檔案.

2.3 常量池

2.3.1 什麼是常量池?

緊接着版本号之後的就是常量池.常量池中存放兩種類型的常量:

  • 字面量 (Literal)

    接近Java語言的常量概念如:文本字元串、final常量值等.

  • 符号引用 (Symbolic Reference)

    屬于編譯原理方面,包括下面三類常量:

  • 類和接口的全限定名
  • 字段的名稱和描述符
  • 方法的名稱和描述符

2.3.2 常量池的特點

  • 常量池長度不固定

    常量池的大小是不固定的,是以常量池開頭放置一個u2類型的無符号數,代表目前常量池的容量.

    注:這個值是從1開始,若為5表示池中有4項常量,索引值1~5.

  • 常量池中的常量由二維表來表示

    開頭有個常量池容量計數值,接下來就全是一個個常量了,隻不過常量都是由一張張二維表構成,除了記錄常量的值以外,還記錄目前常量的相關資訊.

  • 常量池是class檔案的資源倉庫
  • 常量池是與本class中其它部分關聯最多的資料類型
  • 常量池是占用Class檔案空間最大的部分之一 ,也是第一個出現的表類型項目

2.3.3 常量池中常量的類型

剛才介紹了,常量池中的常量大體上分為:字面量 和 符号引用.在此基礎上,根據常量的資料類型不同,又可以被細分為14種常量類型.這14種常量類型都有各自的二維表示結構.每種常量類型的頭1個位元組都是tag,表示目前常量屬于14種類型中的哪一個.

(六)-class檔案結構1 什麼是JVM的“無關性”?2 縱觀Class檔案結構

以CONSTANT_Class_info常量為例,它的二維表示結構如下:

CONSTANT_Class_info表:

[圖檔上傳失敗...(image-847fbb-1513661176215)]

tag表示目前常量的類型(目前常量為CONSTANT_Class_info,是以tag的值應為7,表示一個類或接口的全限定名);

name_index表示這個類或接口全限定名的位置.它的值表示指向常量池的第幾個常量.它會指向一個CONSTANT_Utf8_info類型的常量,它的二維表結構如下:

CONSTANT_Utf8_info表:

[圖檔上傳失敗...(image-c16889-1513661176215)]

CONSTANT_Utf8_info表示字元串常量;

tag表示目前常量的類型,這裡應該是1;

length表示這個字元串的長度;

bytes為這個字元串的内容(采用縮略的UTF8編碼)

問:為什麼Java中定義的類、變量名字必須小于64K?

類、接口、變量等名字都屬于符号引用,它們都存儲在常量池中。而不管哪種符号引用,它們的名字都由CONSTANT_Utf8_info類型的常量表示,這種類型的常量使用u2存儲字元串的長度。由于2位元組最多能表示65535個數,是以這些名字的最大長度最多隻能是64K。

問:什麼是UTF-8編碼?什麼是縮略UTF-8編碼?

前者每個字元使用3個位元組表示,而後者把128個ASKII碼用1位元組表示,某些字元用2位元組表示,某些字元用3位元組表示。

2.4 通路标志

在常量池結束之後是2位元組的通路标志.表示這個class檔案是類還是接口、是否被public修飾、是否被abstract修飾、是否被final修飾等.

由于這些标志都由是/否表示,是以可以用0/1表示.

通路标志為2位元組,可以表示16位标志,但JVM目前隻定義了8種,未定義的直接寫0.

(六)-class檔案結構1 什麼是JVM的“無關性”?2 縱觀Class檔案結構

2.5 類索引、父類索引、接口索引集合

表示目前class檔案所表示類的名字、父類名字、接口們的名字.

它們按照順序依次排列,類索引和父類索引各自使用一個u2類型的無符号常量,這個常量指向CONSTANT_Class_info類型的常量,該常量的bytes字段記錄了本類、父類的全限定名.

由于一個類的接口可能有好多個,是以需要用一個集合來表示接口索引,它在類索引和父類索引之後.這個集合頭兩個位元組表示接口索引集合的長度,接下來就是接口的名字索引.

2.6 字段表的集合

2.6.1 什麼是字段表集合?

用于存儲本類所涉及到的成員變量,包括執行個體變量和類變量,但不包括方法中的局部變量.

每一個字段表隻表示一個成員變量,本類中所有的成員變量構成了字段表集合.

2.6.2 字段表結構的定義

(六)-class檔案結構1 什麼是JVM的“無關性”?2 縱觀Class檔案結構
  • access_flags

    字段的通路标志。在Java中,每個成員變量都有一系列的修飾符,和上述class檔案的通路标志的作用一樣,隻不過成員變量的通路标志與類的通路标志稍有差別。

  • name_index

    本字段名字的索引。指向一個CONSTANT_Class_info類型的常量,這裡面存儲了本字段的名字等資訊。

  • descriptor_index

    描述符。用于描述本字段在Java中的資料類型等資訊(下面詳細介紹)

  • attributes_count

    屬性表集合的長度。

  • attributes

    屬性表集合。到descriptor_index為止是字段表的固定資訊,光有上述資訊可能無法完整地描述一個字段,是以用屬性表集合來存放額外的資訊,比如一個字段的值。(下面會詳細介紹)

2.6.3 什麼是描述符?

成員變量(包括靜态成員變量和執行個體變量) 和 方法都有各自的描述符。

對于字段而言,描述符用于描述字段的資料類型;

對于方法而言,描述符用于描述字段的資料類型、參數清單、傳回值。

在描述符中,基本資料類型用大寫字母表示,對象類型用“L對象類型的全限定名”表示,數組用“[數組類型的全限定名”表示。

描述方法時,将參數根據上述規則放在()中,()右側按照上述方法放置傳回值。而且,參數之間無需任何符号。

2.6.4 字段表集合的注意點

  • 一個class檔案的字段表集合中不能出現從父類/接口繼承而來字段;
  • 一個class檔案的字段表集合中可能會出現程式猿沒有定義的字段

    如編譯器會自動地在内部類的class檔案的字段表集合中添加外部類對象的成員變量,供内部類通路外部類。

  • Java中隻要兩個字段名字相同就無法通過編譯。但在JVM規範中,允許兩個字段的名字相同但描述符不同的情況,并且認為它們是兩個不同的字段。

2.7 方法表的集合

在class檔案中,所有的方法以二維表的形式存儲,每張表來表示一個函數,一個類中的所有方法構成方法表的集合。

方法表的結構和字段表的結構一緻,隻不過通路标志和屬性表集合的可選項有所不同。

(六)-class檔案結構1 什麼是JVM的“無關性”?2 縱觀Class檔案結構

方法表的屬性表集合中有一張Code屬性表,用于存儲目前方法經編譯器編譯過後的位元組碼指令。

方法表集合的注意點

  • 如果本class沒有重寫父類的方法,那麼本class檔案的方法表集合中是不會出現父類/父接口的方法表;
  • 本class的方法表集合可能出現程式猿沒有定義的方法

    編譯器在編譯時會在class檔案的方法表集合中加入類構造器和執行個體構造器。

  • 重載一個方法需要有相同的簡單名稱和不同的特征簽名。JVM的特征簽名和Java的特征簽名有所不同:
  • Java特征簽名:方法參數在常量池中的字段符号引用的集合
  • JVM特征簽名:方法參數+傳回值

2.8 屬性表的集合

下一篇: 指令模式