日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

vs当前文件的函数索引_VM实战(六) - 通过案例深入学习class文件结构原理

發布時間:2025/3/21 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 vs当前文件的函数索引_VM实战(六) - 通过案例深入学习class文件结构原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

0 更多干貨關注 JavaEdge 公眾號

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運行它們.

2 縱觀Class文件結構

class文件包含Java程序執行的字節碼

數據嚴格按照格式緊湊排列在class文件中的二進制流,中間無任何分隔符

文件開頭有一個0xcafebabe(16進制)特殊的一個標志

  • 下圖展示為16進制

class文件是一組以8位字節為基礎單位的二進制流,它的內容具有嚴格的規范,文件中沒有任何分隔符,全是連續的0/1.

class文件中的所有內容被分為兩種類型:無符號數 和 表。

  • 無符號數
  • 基本的數據類型,以u1、u2、u4、u8,分別代表1字節、2字節、4字節、8字節的無符號數.
  • class文件中所有數據(即無符號數)要么單獨存在,要么由多個無符號數組成二維表.即class文件中的數據要么是單個值,要么是二維表.

實踐

  • Demo1.java
  • Demo1.txt
  • 版本號規則: JDK5,6,7,8
  • 分別對應49,50,51,522.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種常量類型,都有各自的二維表示結構

每種常量類型的頭1個字節都是tag,表示當前常量屬于14種類型中的哪一個.

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

CONSTANT_Class_info表

類型名稱數量u1tag1u2name_index1

  • tag 表示當前常量的類型(當前常量為CONSTANT_Class_info,因此tag的值應為7,表一個類或接口的全限定名);
  • name_index 表示這個類或接口全限定名的位置.它的值表示指向常量池的第幾個常量.它會指向一個CONSTANT_Utf8_info類型的常量

類型名稱數量u1tag1u2length1u1byteslength

CONSTANT_Utf8_info表字符串常量

  • tag 表當前常量的類型,這里是1
  • length 表該字符串的長度
  • bytes為這個字符串的內容(采用縮略的UTF8編碼)

Java中定義的類、變量名字必須小于64K

類、接口、變量等名字都屬于符號引用,它們都存儲在常量池中

而不管哪種符號引用,它們的名字都由CONSTANT_Utf8_info類型的常量表示,這種類型的常量使用u2存儲字符串的長度

由于2字節最多能表示65535個數,因此這些名字的最大長度最多只能是64K

UTF-8編碼 VS 縮略UTF-8編碼

前者每個字符使用3個字節表示,而后者把128個ASCII碼用1字節表示,某些字符用2字節表示,某些字符用3字節表示。

  • Demo1.txt中的常量池部分
  • 類信息包含的靜態常量,編譯之后就能確認

2.4 訪問控制在常量池結束之后是2字節的訪問控制

表示這個class文件是類/接口、是否被public/abstract/final修飾等.

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

訪問標志為2字節,可以表示16位標志,但JVM目前只定義了8種,未定義的直接寫0.

  • Demo1.txt中的構造方法

Demo1這個示例中,我們并沒有寫構造函數。

由此可見,沒有定義構造函數時,會有隱式的無參構造函數

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

表示當前class文件所表示類的名字、父類名字、接口們的名字.

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

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

2.6 字段表的集合

2.6.1 什么是字段表集合?

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

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

2.6.2 字段表結構的定義

  • 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規范中,允許兩個字段的名字相同但描述符不同的情況,并且認為它們是兩個不同的字段。
  • Demo1.txt中的程序入口main方法

2.7 方法表的集合

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

方法表的結構和字段表的結構一致,只不過訪問標志和屬性表集合的可選項有所不同。

方法表的屬性表集合中有一張Code屬性表,用于存儲當前方法經編譯器編譯過后的字節碼指令。

方法表集合的注意點

  • 如果本class沒有重寫父類的方法,那么本class文件的方法表集合中是不會出現父類/父接口的方法表;
  • 本class的方法表集合可能出現程序猿沒有定義的方法
  • 編譯器在編譯時會在class文件的方法表集合中加入類構造器和實例構造器。
  • 重載一個方法需要有相同的簡單名稱和不同的特征簽名。JVM的特征簽名和Java的特征簽名有所不同:
  • Java特征簽名:方法參數在常量池中的字段符號引用的集合
  • JVM特征簽名:方法參數+返回值

2.8 屬性表的集合

  • 1
  • 2

程序完整運行分析

總結

我們將JVM運行的核心邏輯進行了詳細剖析。

JVM運行原理中更底層實現,針對不同的操作系統或者處理器,會有不同的實現。

這也是JAVA能夠實現“一處編寫,處處運行”的原因。

開發人員理解到這個層次,就足夠掌握高深的多線程

參考

  • 《碼出高效》

總結

以上是生活随笔為你收集整理的vs当前文件的函数索引_VM实战(六) - 通过案例深入学习class文件结构原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。