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