您好,登錄后才能下訂單哦!
這篇文章主要介紹“JVM入門之什么是Class文件”,在日常操作中,相信很多人在JVM入門之什么是Class文件問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”JVM入門之什么是Class文件”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
Java作為一門編程語言能夠獲得如此廣泛的認可,除了它有結構嚴謹,面向對象的編程語言之外,它還具備一個非常突出的特性:一次編寫
,到處運行
,即編寫的程序可以擺脫硬件平臺束縛,它提供了一種相對安全的內存管理和訪問機制,避免了絕大部分內存泄漏和指針越界問題。
談到jvm,就離不開與jdk和jre的對比,那么它們之間到底有什么區別和聯系呢?
我們先看這樣一幅架構圖,
從集合關系上看,jdk>jre>jvm
,除了范圍上的區別,我們應該了解的是它們所包含的功能上的差別及各自發揮的作用。
jdk
jdk的全稱是Java Development kit
(java開發工具包),我們可以把程序設計語言
、java虛擬機
、java類庫
這三部分統稱為jdk,jdk是用于支持java程序開發的最小環境
。Developer可以很容易的使用里面的方法以減少代碼量,里面同時包含jre和一些開發的小工具(如編譯工具javac),同時包含了jre。
jre
jre的全稱是Java Running Environment
(java運行時環境 ),可以把java類庫API中的javaSE的API子集
和java虛擬機
這兩部分統稱為JRE,JRE是支持java程序運行的標準環境
。
jvm
jvm的全稱java virtual machine
(java 虛擬機),它只認識XXX.class文件
,虛擬機可以識別這種文件的字節碼指令并調用操作系統上的API,正是這個原因,java才可以跨平臺使用
。
不管怎么說,jvm終究是一個軟件
,那么它是怎樣屏蔽底層的操作系統
、硬件
、CPU指令層
的細節呢?我們以Java程序為例來分析它的執行流程。
實現語言無關性的基礎是虛擬機和字節碼的存儲格式,Java虛擬機不與包括Java語言在內的任何程序語言綁定,它只與Class文件這種特定的二進制文件格式所關聯。
Class文件
是Java語言保持良好兼容性的關鍵,那么Class文件的結構是什么呢,存儲那些內容呢?
事實上,Class文件是一組以8字節為基礎單位的二進制流
,各個數據項目嚴格的按照順序緊湊地排列在文件之中,中間沒有添加任何分割符,這使得整個Class文件存儲的內容幾乎全部是程序運行的必要數據,沒有空隙存在。
《Java虛擬機規范》規定了Class文件格式采用一種類似C語言結構體的偽結構來存儲數據,這種偽結構只包含兩種數據類型,即無符號數
和表
。
無符號數
無符號數屬于基本
數據類型
,可以用來描述數字
、索引引用
、數量值
或按照UTF-8編碼構成的字符串值
表
表是由多個無符號數或者其他表作為數據項構成的
復合數據類型
,為了便于區分,所有表的命名的都以_info
結尾。
class文件通過固定的數據結構排列順序并且每種數據結構指定了占用的字節長度來緊湊的在組成了完整的可讀文件,jvm只需要從文件開始的地方一步一步的讀取能夠完全的解析出這個類文件的內容。
來感受一下字節碼文件長啥樣!
在class文件中,前4個字節被稱為魔數
,它能夠唯一確定class文件能否被虛擬機接受。其實,魔數還廣泛應用在GIF、JPEG等文件頭中。
緊接著魔數的4個字節存儲的是Class文件的版本號
,第5和第6個字節是次版本號
,第7和第8個字節是主版本號
。Java的版本號是從45開始的,JDK1.1之后的每個JDK大版本發布的主版本號加1(JDK1.0~1.1使用了45.0~45.3的版本號),《Java虛擬機規范》在Class文件校驗部分明確要求了即使文件格式并未發生變化,虛擬機也必須拒絕執行超過其版本號的Class文件,所以高版本的JDK能向下兼容以前版本的Class文件,但是不能運行以后版本的Class文件。
在魔數、版本號之后,下一個位置存儲的就是常量池
,常量池可以認為是Class文件里的資源倉庫
,它是Class文件結構中與其它項目關聯最多的數據。常量池的前兩個字節占有的位置稱為常量池計數器
(constant_pool_cont),它記錄著常量池的組成元素常量池項
(cp_info)的個數。
常量池計數器是從1開始的,而不是從0開始的,即如果常量池計數器的值constant_pool_count=22
,則后面的cp_info的個數就為21,這是因為在指定class文件規范的時候,將第0項常量空出來是為了滿足某些指向常量池的索引值的數據在特定的情況下表達”不引用任何一個常量池項
“,這種情況下可以將索引值設置為0來表示。
常量池中主要存放兩大類常量:字面量
和符號引用
,字面量可以理解為Java語言層面上的的常量
概念,如文本字符串
、被聲明為final
的常量值等。而符號引用則包括類和結構的全限定名稱
、字段的名稱和描述符
、方法的名稱和描述符
等。
Class文件存儲了方法
、字段
等各種類信息,但是它僅僅是存儲了而已,它是不能反映出方法、字段等信息在內存中的布局。這是因為Java語言并不像C++語言有鏈接的概念,但是Java語言在虛擬機加載時會進行動態的連接
,虛擬機將會從常量池中獲得對應的符號引用
,再在類創建時或運行時進行解析
、翻譯
到具體的內存地址之中。
在常量池結束之后,緊接著的2個字節代表訪問標志
(access_flags),這個標志用于識別一些類或者接口層次的訪問信息
。比如標識一個Class是類還是接口;是否定義為public類型;是否定義為abstract類型;是否被聲明為final。
標志值與標志名稱的對應關系如下:
標志值 | 標志名稱 |
---|---|
0x0001 | ACC_PUBLIC |
0x0010 | ACC_FINAL |
0x0020 | ACC_SUPER |
0x0100 | ACC_INTERFACE |
0x0200 | ACC_ABSTRACT |
0x1000 | ACC_SYNTHETIC |
0x2000 | ACC_ANNOTATION |
0x4000 | ACC_ENUM |
標志名稱就是限定訪問信息的,如ACC_PUBLIC表示為是否為public類型,ACC_FINAL表示為是否被聲明為final,其它的標志類似。
訪問標志結束后,緊接就是索引
,包括類索引
、父類索引
與接口索引集合
,Class文件可以由這三項數據來確定該類型的繼承關系。我們先了解下這三類索引的各有什么作用
類索引
類索引用于確定這個類的全限定名
,通過類的全限定名找到這個類,所以類索引的作用就是為找出class文件所描述的這個類叫什么名字。
父類索引
父類索引用于確定這個類的父類的全限定名
,有Java語言不支持多重繼承,所以除了Object外,其它類的父類索引只有一個。
接口索引的集合
它是用來描述這個類實現哪些接口
,由于接口是多實現的,所以這些實現的接口將會按順序排列在索引集合中。接口索引的集合在入口處會有一個計數器
,它用來表示集合中索引的數量
,如果該類沒有實現接口,則該計數器為0。
Note:類索引、父類索引和接口索引集合指向常量池中的符號引用。
字段表集合用于描述接口
或者類中聲明的變量
,它有若干個字段表組成,字段表集合的就類似一個數組的結構,jvm在編譯類的時候,會將類中的定義的字段的個數統計到字段計數器中,然后將每一個字段信息以結構的形式組成起來放在字段計數器之后。其結
特別需要注意的是,這里的字段包括類變量
以及實例變量
,但是不包括方法內部的聲明的局部變量。
我們在思考這樣一個問題,字段表存儲的是那些信息,這些信息是什么呢,事實上,字段表存儲的就是字段信息,我們整理如下
修飾符(public、protected、private)
實例變量還是類變量(被static修飾)
可變性(final)
并發可見性(volatile)
是否可被序列化(transient)
字段數據類型(基本類型、對象、數組)
字段名稱
既然字段有那么多信息,他的存儲的形式是怎樣的呢?事實上,字段的存儲和我們寫字段的形式是一樣的,不懂?那我們就回顧下!
在字節碼,JVM定義了filed_info結構體來描述字段,它的形式也很簡單,就是一個結構體,
Field_info{ access_flags; name_index; descriptor_index; attribute_count; attributes;}
access_flags
是訪問標志,與前面講解的訪問標志功能是類似的,緊接著access_flags標志的是name_index
和descriptor_index
,它們是對常量池的引用,分別代表著字段的簡單名稱以及字段和方法的描述符。簡單名稱就是指沒有類型和參數修飾的方法或者字段名稱;字段和方法描述符指的是基本類型的頭一個大寫字母,如基本數據類型是byte,則方法描述修飾符是B。attribute_count
表示的屬性計數器,attributes
包含三部分內容(屬性名稱索引、屬性的長度和常量值索引)
方法表集合結構同字段表集合的結構是一樣的,我們這里主要講解它們之間的區別,剩下都可以按照屬性表集合來學習。
區別一
對于方法來說,volatile關鍵字和transient關鍵字是不修飾方法的,所以訪問標志中不會有相應的標志;但是synchronized、native、stricftp和abstract關鍵字是可以修飾方法的,所以在會有相應的訪問標志。
區別二
與字段相比,方法內是有代碼的,那么方法內的代碼存儲到哪里去了呢?事實上,對于方法里的Java代碼,經Javac編譯器編譯成字節碼的指令后,存放在方法屬性表集合中會有一個名為Code的屬性里面。
Class文件的主要結構都說完了,我們從宏觀的角度看看Class文件到底是什么樣,話不多說,來看圖
到此,關于“JVM入門之什么是Class文件”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。