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