JVM规范系列第2章:Java虚拟机结构
本規(guī)范描述的是一種抽象化的虛擬機的行為,而不是任何一種(譯者注:包括 Oracle 公司自己的 HotSpot 和 JRockit 虛擬機)被廣泛使用的虛擬機實現(xiàn)。
記住:JVM規(guī)范是一種高度抽象行為的描述,而不是具體虛擬機的實現(xiàn)。
所有在虛擬機規(guī)范之中沒有明確描述的實現(xiàn)細節(jié),都不應(yīng)成為虛擬機設(shè)計者發(fā)揮創(chuàng)造性的牽絆,設(shè)計者可以完全自主決定所有規(guī)范中不曾描述的虛擬機內(nèi)部細節(jié),例如:運行時數(shù)據(jù)區(qū)的內(nèi)存如何布局、選用哪種垃圾收集的算法、是否要對虛擬機字節(jié)碼指令進行一些內(nèi)部優(yōu)化操作(如使用即時編譯器把字節(jié)碼編譯為機器碼)。
簡單地說,就是《JVM規(guī)范》中提到的內(nèi)容,你一定要遵守。但沒有提到的內(nèi)容,你就自由發(fā)揮。所以我們要知道,我們經(jīng)常聽到的老年代、年輕代、永久代,其實只是HotSpot虛擬機的實現(xiàn)而已。因為《JVM規(guī)范》中并沒有規(guī)定這些東西。
編譯后被 Java 虛擬機所執(zhí)行的代碼使用了一種平臺中立(不依賴于特定硬件及操作系統(tǒng)的)的二進制格式來表示,并且經(jīng)常(但并非絕對)以文件的形式存儲,因此這種格式被稱為 Class文件格式。
實際上字節(jié)碼文件,即Class文件格式使用十六進制編碼,使用二進制格式存儲。
Class 文件格式中精確地定義了類與接口的表示形式, 包括在平臺相關(guān)的目標文件格式中一些細節(jié)上的慣例①,例如字節(jié)序(Byte Ordering)等。
這里又一個計算機的基礎(chǔ)概念:字節(jié)序。有時間可以百度一下搞清楚這個概念。當(dāng)然了,這個并不會影響對于JVM的理解。可以參考這篇文章:https://www.cnblogs.com/broglie/p/5645200.html
JVM的數(shù)據(jù)類型
與 Java 程序語言中的數(shù)據(jù)類型相似,Java 虛擬機可以操作的數(shù)據(jù)類型可分為兩類:原始類型(Primitive Types,也經(jīng)常翻譯為原生類型或者基本類型)和引用類型(Reference Types)。與之對應(yīng),也存在有原始值(Primitive Values)和引用值(Reference Values)兩種類型的數(shù)值。
JVM中也有兩種類型:原始類型、引用類型。
Java 虛擬機希望盡可能多的類型檢查能在程序運行之前完成,換句話說,編譯器應(yīng)當(dāng)在編譯
期間盡最大努力完成可能的類型檢查,使得虛擬機在運行期間無需進行這些操作。
從這句話我們知道:在編譯期間是會及逆行類型檢查的,通過在編譯進行類型檢查,減少JVM運行時的工作量,提高效率。
Java 虛擬機所支持的原始數(shù)據(jù)類型包括了數(shù)值類型(Numeric Types)、 布爾類型(Boolean Type § 2.3.4) 和 returnAddress 類型(§ 2.3.3) 三類。其中數(shù)值類型又分為整型類型(Integral Types, § 2.3.1)和浮點類型(Floating-Point Types, § 2.3.2)兩種。
和Java語言不同,JVM的原始類型包括:數(shù)值類型、布爾類型、returnAddress類型。
- 數(shù)值類型。又分為整數(shù)類型和浮點類型。整數(shù)類型包括:byet、short、int、long、char。浮點類型包括:float類型、double類型。
- 布爾類型。包括:boolean類型,取值為true和false,默認為false。
- returnAddress類型。表示一條字節(jié)碼指令的操作碼。
可以看到在所有的虛擬機支持的原始類型之中,只有 returnAddress 類型是不能直接 Java 語言的數(shù)據(jù)類型對應(yīng)起來的。
整數(shù)類型以及整型值
Java 虛擬機中的整型類型的取值范圍如下:
對于 byte 類型,取值范圍是從-128 至 127(-27至 27-1),包括-128 和 127。
對于 short 類型,取值范圍是從?32768 至 32767(-215至 215-1),包括?32768 和 32767。
對于 int 類型,取值范圍是從?2147483648 至 2147483647(-231至 231-1),包括?2147483648 和 2147483647。
對于 long 類型,取值范圍是從?9223372036854775808 至 9223372036854775807(-263至 263-1),包括?9223372036854775808 和 9223372036854775807。
對于 char 類型, 取值范圍是從 0 至 65535,包括 0 和 65535。
浮點類型的取值及范圍
浮點類型包含 float 類型和 double 類型兩種,它們在概念上與《IEEE Standard for Binary Floating-Point Arithmetic》 ANSI/IEEE Std. 754-1985(IEEE, New York)標準中定義的 32 位單精度和 64 位雙精度 IEEE 754 格式取值和操作都是一致的。
浮點類型包括了 float 類型和 double 類型,它們是在 IEEE 754 標準中定義的。
所有 Java 虛擬機的實現(xiàn)都必須支持兩種標準的浮點數(shù)值集合:單精度浮點數(shù)集合和雙精度浮點數(shù)集合。
所有虛擬機都必須支持單精度浮點數(shù)集合和雙精度浮點數(shù)集合。
關(guān)于浮點數(shù)的內(nèi)容,其實看起來會非常暈。所以我們暫時可以大致看一下就可以,畢竟關(guān)于浮點數(shù)如何定義又是另一個標準了。
returnAddress 類型
returnAddress 類型會被 Java 虛擬機的 jsr、ret 和 jsr_w 指令所使用。jsr、ret、jsr_w 這幾條指令以前主要被使用來實現(xiàn) finally 語句塊,后來改為冗余 finally 塊代碼的方式來實
現(xiàn),甚至到了 JDK7 時,虛擬機已不允許 Class 文件內(nèi)出現(xiàn)這幾條指令。那相應(yīng)地, returnAddress 類型就處于名存實亡的狀態(tài)。
簡單地說,returnAddress類型現(xiàn)在已經(jīng)算是被拋棄了,所以我們只需要大致了解一下有這個東西就好了。
boolean類型
雖然 Java 虛擬機定義了 boolean 這種數(shù)據(jù)類型,但是只對它提供了非常有限的支持。在Java 虛擬機中沒有任何供 boolean 值專用的字節(jié)碼指令,在 Java 語言之中涉及到 boolean類型值的運算,在編譯之后都使用 Java 虛擬機中的 int 數(shù)據(jù)類型來代替。
可以說,我們學(xué)會了int類型,就學(xué)會了boolean類型。因為boolean類型就是簡化版的int類型。boolean的虛擬機指令集都是使用int類型的指令集。
引用類型與值
Java 虛擬機中有三種引用類型:類類型(Class Types)、數(shù)組類型(Array Types)和接口類型(Interface Types)。這些引用類型的值分別由類實例、數(shù)組實例和實現(xiàn)了某個接口的類實例或數(shù)組實例動態(tài)創(chuàng)建。
不僅原始類型不同,JVM的引用類型與原始類型也是不同的。JVM的引用類型有三種:
- 類類型。對應(yīng)的值是類實例。
- 數(shù)組類型。對應(yīng)的值時數(shù)組實例。
- 接口類型。對應(yīng)的值時實現(xiàn)了某個接口的類實例或數(shù)組實例。
嗯,美滋滋啊。又學(xué)到新東西嘞。
數(shù)組類型還包含一個單一維度(即長度不由其類型決定)的組件類型(Component Type),一個數(shù)組的組件類型也可以是數(shù)組。但從任意一個數(shù)組開始,如果發(fā)現(xiàn)其組件類型也是數(shù)組類型的話,繼續(xù)重復(fù)取這個數(shù)組的組件類型,這樣操作不斷執(zhí)行,最終一定可以遇到組件類型不是數(shù)組的情況,這時就把這種類型成為數(shù)組類型的元素類型(Element Type)。數(shù)組的元素類型必須是原始類型、類類型或者接口類型之中的一種。
這段話很繞,其中有幾個關(guān)鍵詞:數(shù)組類型、組件類型(Component Type)。其實這段話意思應(yīng)該是說數(shù)組可以有多維的意思,而數(shù)組里的類型又可以是其他各種類型。大致意思應(yīng)該是如此,但我也不是100%確定。這里MARK一下。TODO
在引用類型的值中還有一個特殊的值: null,當(dāng)一個引用不指向任何對象的時候,它的值就用 null 來表示。Java 虛擬機規(guī)范并沒有規(guī)定 null 在虛擬機實現(xiàn)中應(yīng)當(dāng)怎樣編碼表示。
關(guān)于null的定義,了解一下。
運行時數(shù)據(jù)區(qū)
終于到運行時數(shù)據(jù)區(qū)了,其實這塊就是我們經(jīng)常說的JVM內(nèi)存模型這些東西。但實際上JVM規(guī)范中并沒有這個術(shù)語。JVM規(guī)范只定義了「運行時數(shù)據(jù)區(qū)」這個術(shù)語,指的就是JVM運行時其內(nèi)存的數(shù)據(jù)區(qū)是怎么樣的,應(yīng)該包含哪些東西。具體怎么實現(xiàn),你們各個虛擬機自己打算去。
Java 虛擬機定義了若干種程序運行期間會使用到的運行時數(shù)據(jù)區(qū),其中有一些會隨著虛擬機啟動而創(chuàng)建,隨著虛擬機退出而銷毀。另外一些則是與線程一一對應(yīng)的,這些與線程對應(yīng)的數(shù)據(jù)區(qū)域會隨著線程開始和結(jié)束而創(chuàng)建和銷毀。
為什么我們經(jīng)常用JVM內(nèi)存模型來概括運行時數(shù)據(jù)區(qū),就是因為運行時數(shù)據(jù)區(qū)說的概念太過于分散,沒有聯(lián)系,所以才會有JVM內(nèi)存模型這個詞,讓我們把這些東西聯(lián)系起來,方便記憶。
從上面這段話,我們可以進行一些概括。首先第一句說到:
Java 虛擬機定義了若干種程序運行期間會使用到的運行時數(shù)據(jù)區(qū),其中有一些會隨著虛擬機啟動而創(chuàng)建,隨著虛擬機退出而銷毀。
意思是說有些東西會隨著虛擬機啟動而一直存在,而隨著虛擬機退出而銷毀。而另外一句:
另外一些則是與線程一一對應(yīng)的,這些與線程對應(yīng)的數(shù)據(jù)區(qū)域會隨著線程開始和結(jié)束而創(chuàng)建和銷毀。
上面這句意思是說,又有些數(shù)據(jù)是隨著線程變化的。
其實總結(jié)一下就是:JVM運行時數(shù)據(jù)區(qū)有些數(shù)據(jù)是一直存在的,被所有線程共享的。而有些線程則是線程私有的,隨著線程開始而創(chuàng)建,結(jié)束而銷毀。所以,我們可以將運行時數(shù)據(jù)區(qū)的東西簡單分為兩類:一類是公有的,一類是私有的。
通過這樣一歸類,你對于運行時數(shù)據(jù)區(qū)的概念是不是清晰了許多呢!
可能你還沒感覺到,那是因為到這里你還不知道運行時數(shù)據(jù)區(qū)到底有多少個東西。在這里我先列一下。運行時數(shù)據(jù)區(qū)包括下面幾個部分:
- PC寄存器
- Java虛擬機棧
- Java堆
- 方法區(qū)
- 運行時常量池
- 本地方法棧
好了。記住這 6 個東西還真是困難。那我們用我們上面說的,加個公有私有的分類試試看。
- 公有部分包括:Java堆、方法區(qū)、運行時常量池。
- 私有部分包括:Java虛擬機棧、本地方法棧、PC寄存器。
怎么樣,這樣一歸類,我相信更容易記住了。而且也更符合我們對JVM的理解。JVM的運行時數(shù)據(jù)區(qū)有哪些東西?
首先,有公有和私有兩個部分,公有包括……私有包括……。
大腦天然喜歡結(jié)構(gòu)化的數(shù)據(jù),這種方法才是理解運行時數(shù)據(jù)區(qū)的正確姿勢。
說得有點多了,我們接下來繼續(xù)讀JVM規(guī)范。
PC寄存器
Java 虛擬機可以支持多條線程同時執(zhí)行(可參考《Java 語言規(guī)范》第 17 章),每一條 Java虛擬機線程都有自己的 PC(Program Counter)寄存器。在任意時刻,一條 Java 虛擬機線程只會執(zhí)行一個方法的代碼,這個正在被線程執(zhí)行的方法稱為該線程的當(dāng)前方法。
從這段描述驗證上面我的理解是對的。PC寄存器就是線程私有的,每個線程都有一個PC寄存器。而PC寄存器是用來存儲當(dāng)前線程所執(zhí)行方法的地址。
Java虛擬機棧
每一條 Java 虛擬機線程都有自己私有的 Java 虛擬機棧(Java Virtual Machine Stack)①,這個棧與線程同時創(chuàng)建,用于存儲棧幀(Frames, § 2.6)。
從這段描述我們同樣可以看到,Java虛擬機棧同樣也是線程私有的。Java虛擬機棧的作用就是用來存儲「棧幀」。棧幀這個概念也非常重要,其存儲了調(diào)用方法時,方法的局部變量等信息。后續(xù)會深入學(xué)習(xí)「棧幀」這個概念,這里不做深入介紹。
如果線程請求分配的棧容量超過 Java 虛擬機棧允許的最大容量時, Java 虛擬機將會拋出一個 StackOverflowError 異常。
傳說中的StackOverflowError就是因為這塊區(qū)域出現(xiàn)了問題。
如果 Java 虛擬機棧可以動態(tài)擴展,并且擴展的動作已經(jīng)嘗試過,但是目前無法申請到足夠的內(nèi)存去完成擴展,或者在建立新的線程時沒有足夠的內(nèi)存去創(chuàng)建對應(yīng)的虛擬機棧,那 Java 虛擬機將會拋出一個 OutOfMemoryError 異常。
這里很有意思。意思是說OutOfMemoryError異常,有可能是因為Stack不夠?qū)е碌摹?/p>
我相信許多對于JVM理解不夠深刻的朋友看到這里會很疑惑,因為他們對于JVM內(nèi)存模型的理解就是:Java堆、Java棧。但實際上并不是這樣的。
你應(yīng)該這樣理解:首先,你得知道「運行時數(shù)據(jù)區(qū)」,就是JVM運行時的這塊內(nèi)存。之后,你要知道這塊區(qū)域分為兩個部分:公有和私有。而Java虛擬機棧是分配在堆上的(注意,并不是Java堆),但分配出來的這塊內(nèi)存是線程私有的。
在創(chuàng)建線程是申請創(chuàng)建Java虛擬機棧,可能有兩種情況。第一種,堆上的內(nèi)存不夠了,那么無法創(chuàng)建Java虛擬機棧。這就是上面說到的這種情況,會發(fā)生OutOfMemoryError異常。另一種情況,就是內(nèi)存申請成功了,但是在線程創(chuàng)建后,調(diào)用方法的層次太深了,之前申請的這部分內(nèi)存不夠用了。這時候會發(fā)生StackOverflowError。
說到這兒,你會發(fā)現(xiàn)在《JVM規(guī)范》第28頁末尾寫了這么一段話:
譯者注:請讀者注意避免混淆 Stack、 Heap 和 Java (VM) Stack、 Java Heap 的概念, Java 虛擬機的實現(xiàn)本身是由其他語言編寫的應(yīng)用程序,在 Java 語言程序的角度上看分配在 Java Stack 中的數(shù)據(jù),而在實現(xiàn)虛擬機的程序角度上看則可以是分配在 Heap 之中。
這段話真是不太好理解,但實際上沒那么難。首先,我們的JVM是使用其他語言寫的,那么這個要運行的時候是不是要像系統(tǒng)申請內(nèi)存,那么申請的這一大塊內(nèi)存就是堆內(nèi)存。
在這么大一塊內(nèi)存里,我們又分一部分作為Java堆內(nèi)存、Java虛擬機棧的內(nèi)存。也即是說,JVM中的堆內(nèi)存是系統(tǒng)的堆內(nèi)存中的堆內(nèi)存。站在實現(xiàn)JVM的程序來說,是分配在堆上的。而站在Java程序的角度上看,是分配在Java堆上的。也就是說可以想象這樣一個圖表:
- 系統(tǒng)內(nèi)存有堆內(nèi)存、棧內(nèi)存之分。
- JVM啟動,向系統(tǒng)申請一塊內(nèi)存,系統(tǒng)會分配一塊堆內(nèi)存給它。當(dāng)其他應(yīng)用程序,例如微信啟動,系統(tǒng)也會分配一塊堆內(nèi)存給它。
- JVM獲得了內(nèi)存,于是加載類、運行程序。JVM會在獲得的內(nèi)存分出一部分內(nèi)存作為Java堆,用于分配對象。
- 當(dāng)創(chuàng)建線程,那么JVM會在系統(tǒng)給它的、還未分配的內(nèi)存劃出一部分創(chuàng)建Java虛擬機棧,從而線程創(chuàng)建成功。
所以如果增加Java堆的大小,那么可以分配給Java虛擬機棧的內(nèi)存就變少,那么可以創(chuàng)建的線程就減少。那么就更容易導(dǎo)致StackOverflowError。
Java堆
在 Java 虛擬機中,堆(Heap)是可供各條線程共享的運行時內(nèi)存區(qū)域,也是供所有類實例和數(shù)組對象分配內(nèi)存的區(qū)域。
Java堆是所有線程共享的,存放類實例和數(shù)組對象。
方法區(qū)
在 Java 虛擬機中,方法區(qū)(Method Area) 是可供各條線程共享的運行時內(nèi)存區(qū)域。方法區(qū)與傳統(tǒng)語言中的編譯代碼儲存區(qū)(Storage Area Of Compiled Code)或者操作系統(tǒng)進程的正文段(Text Segment)的作用非常類似,它存儲了每一個類的結(jié)構(gòu)信息,例如運行時常量池(Runtime Constant Pool)、字段和方法數(shù)據(jù)、構(gòu)造函數(shù)和普通方法的字節(jié)碼內(nèi)容、還包括一些在類、實例、接口初始化時用到的特殊方法(§ 2.9)。
可以看到方法區(qū)也是共享的,而且存放的是類的信息。在HotSpot虛擬機中,JDK1.7版本稱其為永久代(Permanent Generation),而在JDK1.8則稱之為元空間(Metaspace)。
運行時常量池
每一個運行時常量池都分配在 Java 虛擬機的方法區(qū)之中(§ 2.5.4),在類和接口被加載到虛擬機后,對應(yīng)的運行時常量池就被創(chuàng)建出來。
運行時常量池分配在JVM的方法區(qū)之中。
本地方法棧
Java 虛擬機實現(xiàn)可能會使用到傳統(tǒng)的棧(通常稱之為“C Stacks”)來支持 native 方法(指使用 Java 以外的其他語言編寫的方法)的執(zhí)行,這個棧就是本地方法棧(Native Method Stack)。當(dāng) Java 虛擬機使用其他語言(例如 C 語言)來實現(xiàn)指令集解釋器時,也會使用到本地方法棧。
本地方法指的是使用Java以外的其他語言編寫的代碼,因為有些時候Java無法直接操作一些底層資源,只能通過C或匯編操作。因此需要通過本地方法來實現(xiàn)。
而本地方法棧就是設(shè)計用來調(diào)用這些非Java語言方法的,其作用與Java虛擬機棧類似。會存放對應(yīng)的局部變量信息、返回結(jié)果等。
本地方法棧同樣會發(fā)生StackOverFlowError和OutOfMemoryError異常。
棧幀
棧幀(Frame)是用來存儲數(shù)據(jù)和部分過程結(jié)果的數(shù)據(jù)結(jié)構(gòu),同時也被用來處理動態(tài)鏈接(Dynamic Linking)、方法返回值和異常分派(Dispatch Exception)。
這里重點記住:存儲數(shù)據(jù)和部分結(jié)果,存儲的部分數(shù)據(jù)就包括了局部變量。
棧幀隨著方法調(diào)用而創(chuàng)建,隨著方法結(jié)束而銷毀——無論方法是正常完成還是異常完成(拋出了在方法內(nèi)未被捕獲的異常)都算作方法結(jié)束。
棧幀隨著方法調(diào)用而創(chuàng)建,是線程私有的。
棧幀的存儲空間分配在 Java 虛擬機棧(§ 2.5.5)之中,每一個棧幀都有自己的局部變量表(Local Variables, § 2.6.1)、操作數(shù)棧(Operand Stack, § 2.6.2)和指向當(dāng)前方法所屬的類的運行時常量池(§ 2.5.5)的引用。
棧幀分配在Java虛擬機棧,而Java虛擬機棧是私有的,所以棧幀肯定也是私有的。可以這么說,棧幀就是Java虛擬機棧里的一個個元素,每次調(diào)用一個方法就push一個棧幀,調(diào)用完畢則poll一個棧幀。
這里還說到,每一個棧幀都有自己的:
- 局部變量表
- 操作數(shù)棧
- 指向當(dāng)前方法所屬類的運行時常量池引用
在一條線程之中,只有目前正在執(zhí)行的那個方法的棧幀是活動的。這個棧幀就被稱為是當(dāng)前棧幀(Current Frame),這個棧幀對應(yīng)的方法就被稱為是當(dāng)前方法(Current Method),定義這個方法的類就稱作當(dāng)前類(Current Class)。
了解下「當(dāng)前棧幀」、「當(dāng)前方法」、「當(dāng)前類」的概念。
請讀者特別注意,棧幀是線程本地私有的數(shù)據(jù),不可能在一個棧幀之中引用另外一條線程的棧幀。
這里原文明確指出:棧幀是線程本地私有的數(shù)據(jù)!
局部變量表
棧幀中局部變量表的長度由編譯期決定,并且存儲于類和接口的二進制表示之中,既通過方法的Code 屬性(§ 4.7.3)保存及提供給棧幀使用。
局部變量表由編譯期決定,并存在方法的Code屬性。
局部變量使用索引來進行定位訪問,第一個局部變量的索引值為零,局部變量的索引值是從零至小于局部變量表最大容量的所有整數(shù)。
局部變量表的索引從零開始。
特別地,當(dāng)一個實例方法被調(diào)用的時候,第 0 個局部變量一定是用來存儲被調(diào)用的實例方法所在的對象的引用(即 Java 語言中的“this”關(guān)鍵字)。
局部變量第一個局部變量,一定是當(dāng)前對象的引用,即this關(guān)鍵字。
操作數(shù)棧
每一個棧幀(§ 2.6)內(nèi)部都包含一個稱為操作數(shù)棧(Operand Stack)的后進先出(Last-In-First-Out, LIFO)棧。
這里要注意一下,Java虛擬機棧包含了棧幀,棧幀包含了操作數(shù)棧,就像下面這樣的關(guān)系:
Java虛擬機棧 -> 棧幀 -> 操作數(shù)棧它們都是棧的數(shù)據(jù)結(jié)構(gòu),操作數(shù)棧也是如此。
只不過Java虛擬機棧是存儲一個個線程的信息,棧幀存儲的事一個個方法的信息,操作數(shù)棧是存儲方法調(diào)用中一個個操作數(shù)的信息。
每一個操作數(shù)棧的成員(Entry) 可以保存一個 Java 虛擬機中定義的任意數(shù)據(jù)類型的值,包括 long 和 double 類型。
操作數(shù)棧的成員可以是任意數(shù)據(jù)類型。
這章節(jié)接下來的關(guān)于浮點數(shù)、浮點算法部分,太過于復(fù)雜,而且實用性太差。這里不深入了解,直接簡單了解就可以了。
在 Java 虛擬機層面上, Java 語言中的構(gòu)造函數(shù)在《Java 語言規(guī)范 (第三版)》(下文簡稱JLS3, § 8.8)是以一個名為的特殊實例初始化方法的形式出現(xiàn)的, 這個方法名稱是由編譯器命名的,因為它并非一個合法的 Java 方法名字,不可能通過程序編碼的方式實現(xiàn)。
<init>這個特殊的初始化方法,是在虛擬機層面存在的,而不是程序編碼層面的。可以通過虛擬機的invokespecial指令調(diào)用。
一個類或者接口最多可以包含不超過一個類或接口的初始化方法,類或者接口就是通過這個方法完成初始化的(§ 5.5)。這個方法是一個不包含參數(shù)的靜態(tài)方法,名為①。這個名字也是由編譯器命名的,因為它并非一個合法的 Java 方法名字,不可能通過程序編碼的方式實現(xiàn)。
與init類似clinit也是一個虛擬機層面的方法,不是程序編碼層面的。但類或接口的clinit方法只能由虛擬機自身隱式調(diào)用,沒有任何虛擬機字節(jié)碼指令可以調(diào)用這個方法。
字節(jié)碼指令
Java 虛擬機的指令由一個字節(jié)長度的、代表著某種特定操作含義的操作(Opcode)以及跟隨其后的零至多個代表此操作所需參數(shù)的操作數(shù)(Operands)所構(gòu)成。
字節(jié)碼指令組成為:操作碼+操作數(shù)。其中操作碼為一個字節(jié)長度,操作數(shù)情況由操作碼決定。
大部分的指令都沒有支持整數(shù)類型 byte、 char 和 short,甚至
沒有任何指令支持 boolean 類型。編譯器會在編譯期或運行期會將 byte 和 short 類型的數(shù)據(jù)帶符號擴展(Sign-Extend)為相應(yīng)的 int 類型數(shù)據(jù),將 boolean 和 char 類型數(shù)據(jù)零位擴展(Zero-Extend)為相應(yīng)的 int 類型數(shù)據(jù)。與之類似的,在處理 boolean、 byte、 short 和char 類型的數(shù)組時,也會轉(zhuǎn)換為使用對應(yīng)的 int 類型的字節(jié)碼指令來處理。
在虛擬機中,byte/short/boolean/char 都是用 int 類型來存儲的。規(guī)范的下一句也直接指明了。
因此,大多數(shù)對于boolean、 byte、 short 和 char 類型數(shù)據(jù)的操作,實際上都是使用相應(yīng)的對 int 類型作為運算類型(Computational Type)。
虛擬機指令集大致可以分為下面幾類:
- 加載和存儲指令
- 運算指令
- 類型轉(zhuǎn)換指令
- 對象創(chuàng)建于操作
- 操作數(shù)棧管理指令
- 控制轉(zhuǎn)移指令
- 方法調(diào)用和返回指令
- 拋出異常
- 同步
因為虛擬機指令集很枯燥,就像linux命令一樣,所以這里不深入講。有需要的時候再一個個查就可以了。
JVM規(guī)范系列文章目錄
- JVM規(guī)范系列開篇:為什么要讀JVM規(guī)范?
- JVM規(guī)范系列第1章:引言
- JVM規(guī)范系列第2章:Java虛擬機結(jié)構(gòu)
- JVM規(guī)范系列第3章:為Java虛擬機編譯
- JVM規(guī)范系列第4章:Class文件格式
- JVM規(guī)范系列第5章:加載、鏈接與初始化
- JVM規(guī)范系列第6章:Java虛擬機指令集
- JVM規(guī)范系列:總結(jié)
轉(zhuǎn)載于:https://www.cnblogs.com/chanshuyi/p/jvm_specification_02_jvm_structure.html
總結(jié)
以上是生活随笔為你收集整理的JVM规范系列第2章:Java虚拟机结构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【译】一份通俗易懂的React.js基础
- 下一篇: bzoj 1069 [SCOI2007]