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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【深入理解JVM】JVM字节码指令集

發(fā)布時(shí)間:2024/4/14 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【深入理解JVM】JVM字节码指令集 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Java 虛擬機(jī)的指令由一個(gè)字節(jié)長(zhǎng)度的、代表著某種特定操作含義的操作碼(Opcode)以及跟隨其后的零至多個(gè)代表此操作所需參數(shù)的操作數(shù)(Operands)所構(gòu)成。虛擬機(jī)中許多指令并不包含操作數(shù),只有一個(gè)操作碼。

如果忽略異常處理,那 Java 虛擬機(jī)的解釋器使用下面這個(gè)偽代碼的循環(huán)即可有效地工作:

do{自動(dòng)計(jì)算PC寄存器以及從PC寄存器的位置取出操作碼;if(存在操作數(shù))取出操作數(shù);執(zhí)行操作碼所定義的操作 }while(處理下一次循環(huán));

操作數(shù)的數(shù)量以及長(zhǎng)度取決于操作碼,如果一個(gè)操作數(shù)的長(zhǎng)度超過(guò)了一個(gè)字節(jié),那它將會(huì)以 Big-Endian 順序存儲(chǔ)——即高位在前的字節(jié)序。舉個(gè)例子,如果要將一個(gè) 16 位長(zhǎng)度的無(wú)符號(hào)整數(shù)使用兩個(gè)無(wú)符號(hào)字節(jié)存儲(chǔ)起來(lái)(將它們命名為 byte1 和 byte2),那它們的值應(yīng)該是這樣的:

(byte1<<8)|byte2

字節(jié)碼指令流應(yīng)當(dāng)都是單字節(jié)對(duì)齊的,只有“tableswitch”和“l(fā)ookupswitch”兩條指令例外,由于它們的操作數(shù)比較特殊,都是以 4 字節(jié)為界劃分開(kāi)的,所以這兩條指令那個(gè)也需要預(yù)留出相應(yīng)的空位來(lái)實(shí)現(xiàn)對(duì)齊。

限制 Java 虛擬機(jī)操作碼的長(zhǎng)度為一個(gè)字節(jié),并且放棄了編譯后代碼的參數(shù)長(zhǎng)度對(duì)齊,是為了盡可能地獲得短小精干的編譯代碼,即使這可能會(huì)讓 Java 虛擬機(jī)的具體實(shí)現(xiàn)付出一定的性能成本為代價(jià)。由于每個(gè)操作碼只能有一個(gè)字節(jié)長(zhǎng)度,所以直接限制了整個(gè)指令集的數(shù)量 (字節(jié)碼無(wú)法超過(guò) 256 條的限制就來(lái)源于此) ,又由于沒(méi)有假設(shè)數(shù)據(jù)是對(duì)齊好的,這就意味著虛擬機(jī)處理那些超過(guò)一個(gè)字節(jié)的數(shù)據(jù)的時(shí)候,不得不在運(yùn)行時(shí)從字節(jié)中重建出具體數(shù)據(jù)的結(jié)構(gòu),這在某種程度上會(huì)損失一些性能。

1、數(shù)據(jù)類(lèi)型與Java虛擬機(jī)

在 Java 虛擬機(jī)的指令集中,大多數(shù)的指令都包含了其操作所對(duì)應(yīng)的數(shù)據(jù)類(lèi)型信息。舉個(gè)例子,iload 指令用于從局部變量表中加載 int 型的數(shù)據(jù)到操作數(shù)棧中,而 fload 指令加載的則是 float 類(lèi)型的數(shù)據(jù)。這兩條指令的操作可能會(huì)是由同一段代碼來(lái)實(shí)現(xiàn)的,但它們必須擁有各自獨(dú)立的操作符。

對(duì)于大部分為與數(shù)據(jù)類(lèi)型相關(guān)的字節(jié)碼指令,他們的操作碼助記符中都有特殊的字符來(lái)表明專(zhuān)門(mén)為哪種數(shù)據(jù)類(lèi)型服務(wù):i 代表對(duì) int 類(lèi)型的數(shù)據(jù)操作,l 代表 long,s 代表 short,b 代表 byte,c 代表 char,f 代表 float,d 代表 double,a 代表 reference。也有一些指令的助記符中沒(méi)有明確的指明操作類(lèi)型的字母,例如 arraylength 指令,它沒(méi)有代表數(shù)據(jù)類(lèi)型的特殊字符,但操作數(shù)永遠(yuǎn)只能是一個(gè)數(shù)組類(lèi)型的對(duì)象。還有另外一些指令,例如無(wú)條件跳轉(zhuǎn)指令 goto 則是與數(shù)據(jù)類(lèi)型無(wú)關(guān)的。

由于 Java 虛擬機(jī)的操作碼長(zhǎng)度只有一個(gè)字節(jié),所以包含了數(shù)據(jù)類(lèi)型的操作碼對(duì)指令集的設(shè)計(jì)帶來(lái)了很大的壓力:如果每一種與數(shù)據(jù)類(lèi)型相關(guān)的指令都支持 Java 虛擬機(jī)所有運(yùn)行時(shí)數(shù)據(jù)類(lèi)型的話,那恐怕就會(huì)超出一個(gè)字節(jié)所能表示的數(shù)量范圍了。因此,Java 虛擬機(jī)的指令集對(duì)于特定的操作只提供了有限的類(lèi)型相關(guān)指令去支持它,換句話說(shuō),指令集將會(huì)故意被設(shè)計(jì)成非完全獨(dú)立的(Not Orthogonal,即并非每種數(shù)據(jù)類(lèi)型和每一種操作都有對(duì)應(yīng)的指令)。有一些單獨(dú)的指令可以在必要的時(shí)候用來(lái)將一些不支持的類(lèi)型轉(zhuǎn)換為可被支持的類(lèi)型。

下表列舉了 Java 虛擬機(jī)所支持的字節(jié)碼指令集,通過(guò)使用數(shù)據(jù)類(lèi)型列所代表的特殊字符替換 opcode 列的指令模板中的 T,就可以得到一個(gè)具體的字節(jié)碼指令。如果在表中指令模板與數(shù)據(jù)類(lèi)型兩列共同確定的格為空,則說(shuō)明虛擬機(jī)不支持對(duì)這種數(shù)據(jù)類(lèi)型執(zhí)行這項(xiàng)操作。例如 load 指令有操作 int 類(lèi)型的 iload,但是沒(méi)有操作 byte 類(lèi)型的同類(lèi)指令。

請(qǐng)注意,從下表中看來(lái),大部分的指令都沒(méi)有支持整數(shù)類(lèi)型 byte、char 和 short,甚至沒(méi)有任何指令支持 boolean 類(lèi)型。編譯器會(huì)在編譯期或運(yùn)行期會(huì)將 byte 和 short 類(lèi)型的數(shù)據(jù)帶符號(hào)擴(kuò)展(Sign-Extend)為相應(yīng)的 int 類(lèi)型數(shù)據(jù),將 boolean 和 char 類(lèi)型數(shù)據(jù)零位擴(kuò)展(Zero-Extend)為相應(yīng)的 int 類(lèi)型數(shù)據(jù)。與之類(lèi)似的,在處理 boolean、byte、short 和 char 類(lèi)型的數(shù)組時(shí),也會(huì)轉(zhuǎn)換為使用對(duì)應(yīng)的 int 類(lèi)型的字節(jié)碼指令來(lái)處理。因此,大多數(shù)對(duì)于 boolean、byte、short 和 char 類(lèi)型數(shù)據(jù)的操作,實(shí)際上都是使用相應(yīng)的對(duì) int 類(lèi)型作為運(yùn)算類(lèi)型(Computational Type)。

Java 虛擬機(jī)指令集所支持的數(shù)據(jù)類(lèi)型:

在 Java 虛擬機(jī)中,實(shí)際類(lèi)型與運(yùn)算類(lèi)型之間的映射關(guān)系,如下表所示:

有部分對(duì)操作棧進(jìn)行操作的 Java 虛擬機(jī)指令(例如 pop 和 swap 指令)是與具體類(lèi)型無(wú)關(guān)的,不過(guò)這些指令也必須受到運(yùn)算類(lèi)型分類(lèi)的限制,這些分類(lèi)也在表中列出了。

2、加載和存儲(chǔ)指令

加載和存儲(chǔ)指令用于將數(shù)據(jù)從棧幀的局部變量表和操作數(shù)棧之間來(lái)回傳輸:

1、將一個(gè)局部變量加載到操作棧的指令包括有:iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>2、將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表的指令包括有:istore、istore_<n>、lstore、lstore_<n>、fstore、fstore_<n>、dstore、dstore_<n>、astore、astore_<n>3、將一個(gè)常量加載到操作數(shù)棧的指令包括有:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>4、擴(kuò)充局部變量表的訪問(wèn)索引的指令:wide

訪問(wèn)對(duì)象的字段或數(shù)組元素的指令也同樣會(huì)與操作數(shù)棧傳輸數(shù)據(jù)。

上面所列舉的指令助記符中,有一部分是以尖括號(hào)結(jié)尾的(例如 iload_<n>),這些指令助記符實(shí)際上是代表了一組指令(例如 iload_<n>,它代表了 iload_0、iload_1、iload_2 和 iload_3 這幾條指令)。這幾組指令都是某個(gè)帶有一個(gè)操作數(shù)的通用指令(例如 iload)的特殊形式,對(duì)于這若干組特殊指令來(lái)說(shuō),它們表面上沒(méi)有操作數(shù),不需要進(jìn)行取操作數(shù)的動(dòng)作,但操作數(shù)都是在指令中隱含的。除此之外,他們的語(yǔ)義與原生的通用指令完全一致(例如 iload_0 的語(yǔ)義與操作數(shù)為 0 時(shí)的 iload 指令語(yǔ)義完全一致)。在尖括號(hào)之間的字母制定了指令隱含操作數(shù)的數(shù)據(jù)類(lèi)型,<i>代表是 int 形數(shù)據(jù),<l>代表 long 型,<f>代表 float 型,<d>代表 double型。在操作 byte、char 和 short 類(lèi)型數(shù)據(jù)時(shí),也用 int 類(lèi)型表示。

這種指令表示方法,在整個(gè)《Java 虛擬機(jī)規(guī)范》之中都是通用的。

3、運(yùn)算指令

算術(shù)指令用于對(duì)兩個(gè)操作數(shù)棧上的值進(jìn)行某種特定運(yùn)算,并把結(jié)果重新存入到操作棧頂。大體上運(yùn)算指令可以分為兩種:對(duì)整型數(shù)據(jù)進(jìn)行運(yùn)算的指令與對(duì)浮點(diǎn)型數(shù)據(jù)進(jìn)行運(yùn)算的指令,無(wú)論是那種算術(shù)指令,都是使用 Java 虛擬機(jī)的數(shù)字類(lèi)型的。數(shù)據(jù)沒(méi)有直接支持 byte、short、char 和 boolean 類(lèi)型(§2.11.1)的算術(shù)指令,對(duì)于這些數(shù)據(jù)的運(yùn)算,都是使用操作 int 類(lèi)型的指令。

整數(shù)與浮點(diǎn)數(shù)的算術(shù)指令在溢出和被零除的時(shí)候也有各自不同的行為,所有的算術(shù)指令包括:

加法指令:iadd、ladd、fadd、dadd減法指令:isub、lsub、fsub、dsub乘法指令:imul、lmul、fmul、dmul除法指令:idiv、ldiv、fdiv、ddiv求余指令:irem、lrem、frem、drem取反指令:ineg、lneg、fneg、dneg位移指令:ishl、ishr、iushr、lshl、lshr、lushr按位或指令:ior、lor按位與指令:iand、land按位異或指令:ixor、lxor局部變量自增指令:iinc比較指令:dcmpg、dcmpl、fcmpg、fcmpl、lcmp

Java 虛擬機(jī)的指令集直接支持了在《Java 語(yǔ)言規(guī)范》中描述的各種對(duì)整數(shù)及浮點(diǎn)數(shù)操作的語(yǔ)義。

Java 虛擬機(jī)沒(méi)有明確規(guī)定整型數(shù)據(jù)溢出的情況,但是規(guī)定了在處理整型數(shù)據(jù)時(shí),只有除法指令(idiv 和 ldiv)以及求余指令(irem 和 lrem)出現(xiàn)除數(shù)為零時(shí)會(huì)導(dǎo)致虛擬機(jī)拋出異常,如果發(fā)生了這種情況,虛擬機(jī)將會(huì)拋出 ArithmeitcException 異常。

Java 虛擬機(jī)在處理浮點(diǎn)數(shù)時(shí),必須遵循 IEEE 754 規(guī)范中所規(guī)定行為限制。也就是說(shuō) Java虛擬機(jī)要求完全支持 IEEE 754 中定義的非正規(guī)浮點(diǎn)數(shù)值(Denormalized Floating-Point Numbers,§2.3.2)和逐級(jí)下溢(Gradual Underflow)。這些特征將會(huì)使得某些數(shù)值算法處理起來(lái)變得更容易一些。

Java 虛擬機(jī)要求在進(jìn)行浮點(diǎn)數(shù)運(yùn)算時(shí),所有的運(yùn)算結(jié)果都必須舍入到適當(dāng)?shù)倪M(jìn)度,非精確的結(jié)果必須舍入為可被表示的最接近的精確值,如果有兩種可表示的形式與該值一樣接近,那將優(yōu)先選擇最低有效位為零的。這種舍入模式也是 IEEE 754 規(guī)范中的默認(rèn)舍入模式,稱(chēng)為向最接近數(shù)舍入模式。

在把浮點(diǎn)數(shù)轉(zhuǎn)換為整數(shù)時(shí),Java 虛擬機(jī)使用 IEEE 754 標(biāo)準(zhǔn)中的向零舍入模式,這種模式的舍入結(jié)果會(huì)導(dǎo)致數(shù)字被截?cái)?#xff0c;所有小數(shù)部分的有效字節(jié)都會(huì)被丟棄掉。向零舍入模式將在目標(biāo)數(shù)值類(lèi)型中選擇一個(gè)最接近,但是不大于原值的數(shù)字來(lái)作為最精確的舍入結(jié)果。

Java 虛擬機(jī)在處理浮點(diǎn)數(shù)運(yùn)算時(shí),不會(huì)拋出任何運(yùn)行時(shí)異常(這里所講的是 Java 的異常,請(qǐng)勿與 IEEE 754 規(guī)范中的浮點(diǎn)異常互相混淆),當(dāng)一個(gè)操作產(chǎn)生溢出時(shí),將會(huì)使用有符號(hào)的無(wú)窮大來(lái)表示,如果某個(gè)操作結(jié)果沒(méi)有明確的數(shù)學(xué)定義的話,將會(huì)時(shí)候 NaN 值來(lái)表示。所有使用 NaN 值作為操作數(shù)的算術(shù)操作,結(jié)果都會(huì)返回 NaN。

在對(duì) long 類(lèi)型數(shù)值進(jìn)行比較時(shí),虛擬機(jī)采用帶符號(hào)的比較方式,而對(duì)浮點(diǎn)數(shù)值進(jìn)行比較時(shí)(dcmpg、dcmpl、fcmpg、fcmpl),虛擬機(jī)采用 IEEE 754 規(guī)范說(shuō)定義的無(wú)信號(hào)比較(Nonsignaling Comparisons)方式。

4、類(lèi)型轉(zhuǎn)換指令

類(lèi)型轉(zhuǎn)換指令可以將兩種 Java 虛擬機(jī)數(shù)值類(lèi)型進(jìn)行相互轉(zhuǎn)換,這些轉(zhuǎn)換操作一般用于實(shí)現(xiàn)用戶代碼的顯式類(lèi)型轉(zhuǎn)換操作,或者用來(lái)處理 Java 虛擬機(jī)字節(jié)碼指令集中指令非完全獨(dú)立獨(dú)立的問(wèn)題。

Java 虛擬機(jī)直接支持(注:“直接支持”意味著轉(zhuǎn)換時(shí)無(wú)需顯式的轉(zhuǎn)換指令)以下數(shù)值的寬化類(lèi)型轉(zhuǎn)換(Widening Numeric Conversions,小范圍類(lèi)型向大范圍類(lèi)型的安全轉(zhuǎn)換):

int 類(lèi)型到 longfloat 或者 double 類(lèi)型long 類(lèi)型到 floatdouble 類(lèi)型float 類(lèi)型到 double 類(lèi)型

窄化類(lèi)型轉(zhuǎn)換(Narrowing Numeric Conversions)指令包括有:i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l 和 d2f。窄化類(lèi)型轉(zhuǎn)換可能會(huì)導(dǎo)致轉(zhuǎn)換結(jié)果產(chǎn)生不同的正負(fù)號(hào)、不同的數(shù)量級(jí),轉(zhuǎn)換過(guò)程很可能會(huì)導(dǎo)致數(shù)值丟失精度。

在將 int 或 long 類(lèi)型窄化轉(zhuǎn)換為整數(shù)類(lèi)型 T 的時(shí)候,轉(zhuǎn)換過(guò)程僅僅是簡(jiǎn)單的丟棄除最低位N 個(gè)字節(jié)以外的內(nèi)容,N 是類(lèi)型 T 的數(shù)據(jù)類(lèi)型長(zhǎng)度,這將可能導(dǎo)致轉(zhuǎn)換結(jié)果與輸入值有不同的正負(fù)號(hào)(注:在高位字節(jié)符號(hào)位被丟棄了)。

在將一個(gè)浮點(diǎn)值轉(zhuǎn)窄化轉(zhuǎn)換為整數(shù)類(lèi)型 T(T 限于 int 或 long 類(lèi)型之一)的時(shí)候,將遵循以下轉(zhuǎn)換規(guī)則:

如果浮點(diǎn)值是 NaN,那轉(zhuǎn)換結(jié)果就是 int 或 long 類(lèi)型的 0,否則,如果浮點(diǎn)值不是無(wú)窮大的話,浮點(diǎn)值使用 IEEE 754 的向零舍入模式取整,獲得整數(shù)值 v,這時(shí)候可能有兩種情況:

如果 T 是 long 類(lèi)型,并且轉(zhuǎn)換結(jié)果在 long 類(lèi)型的表示范圍之內(nèi),那就轉(zhuǎn)換為 long類(lèi)型數(shù)值 v;

如果 T 是 int 類(lèi)型,并且轉(zhuǎn)換結(jié)果在 int 類(lèi)型的表示范圍之內(nèi),那就轉(zhuǎn)換為 int類(lèi)型數(shù)值 v;

否則:
如果轉(zhuǎn)換結(jié)果 v 的值太小(包括足夠小的負(fù)數(shù)以及負(fù)無(wú)窮大的情況),無(wú)法使用 T 類(lèi)型表示的話,那轉(zhuǎn)換結(jié)果取 int 或 long 類(lèi)型所能表示的最小數(shù)字。

如果轉(zhuǎn)換結(jié)果 v 的值太大(包括足夠大的正數(shù)以及正無(wú)窮大的情況),無(wú)法使用 T 類(lèi)型表示的話,那轉(zhuǎn)換結(jié)果取 int 或 long 類(lèi)型所能表示的最大數(shù)字。

從 double 類(lèi)型到 float 類(lèi)型做窄化轉(zhuǎn)換的過(guò)程與 IEEE 754 中定義的一致,通過(guò) IEEE 754向最接近數(shù)舍入模式(§2.8.1)舍入得到一個(gè)可以使用 float 類(lèi)型表示的數(shù)字。如果轉(zhuǎn)換結(jié)果的絕對(duì)值太小無(wú)法使用 float 來(lái)表示的話,將返回 float 類(lèi)型的正負(fù)零。如果轉(zhuǎn)換結(jié)果的絕對(duì)值太大無(wú)法使用 float 來(lái)表示的話,將返回 float 類(lèi)型的正負(fù)無(wú)窮大,對(duì)于 double 類(lèi)型的 NaN值將就規(guī)定轉(zhuǎn)換為 float 類(lèi)型的 NaN 值。

盡管可能發(fā)生上限溢出、下限溢出和精度丟失等情況,但是 Java 虛擬機(jī)中數(shù)值類(lèi)型的窄化轉(zhuǎn)換永遠(yuǎn)不可能導(dǎo)致虛擬機(jī)拋出運(yùn)行時(shí)異常(此處的異常是指《Java 虛擬機(jī)規(guī)范》中定義的異常,請(qǐng)讀者不要與 IEEE 754 中定義的浮點(diǎn)異常信號(hào)產(chǎn)生混淆)。

5、對(duì)象創(chuàng)建與操作

雖然類(lèi)實(shí)例和數(shù)組都是對(duì)象,但 Java 虛擬機(jī)對(duì)類(lèi)實(shí)例和數(shù)組的創(chuàng)建與操作使用了不同的字節(jié)碼指令:

1、創(chuàng)建類(lèi)實(shí)例的指令:new

2、創(chuàng)建數(shù)組的指令:newarray,anewarray,multianewarray

3、訪問(wèn)類(lèi)字段(static 字段,或者稱(chēng)為類(lèi)變量)和實(shí)例字段(非 static 字段,或者成為實(shí)例變量)的指令:getfield、putfield、getstatic、putstatic

4、把一個(gè)數(shù)組元素加載到操作數(shù)棧的指令:baload、caload、saload、iaload、laload、faload、daload、aaload

5、將一個(gè)操作數(shù)棧的值儲(chǔ)存到數(shù)組元素中的指令:bastore、castore、sastore、iastore、fastore、dastore、aastore

6、取數(shù)組長(zhǎng)度的指令:arraylength

7、檢查類(lèi)實(shí)例類(lèi)型的指令:instanceof、checkcas

6、操作數(shù)棧管理指令

Java 虛擬機(jī)提供了一些用于直接操作操作數(shù)棧的指令,包括:pop、pop2、dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2 和 swap。

7、控制轉(zhuǎn)移指令

控制轉(zhuǎn)移指令可以讓 Java 虛擬機(jī)有條件或無(wú)條件地從指定指令而不是控制轉(zhuǎn)移指令的下一條指令繼續(xù)執(zhí)行程序。控制轉(zhuǎn)移指令包括有:

1、條件分支:ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、if_icmplt, if_icmpgt、if_icmple、if_icmpge、if_acmpeq 和 if_acmpne。

2、復(fù)合條件分支:tableswitch、lookupswitch

3、無(wú)條件分支:goto、goto_w、jsr、jsr_w、ret

在 Java 虛擬機(jī)中有專(zhuān)門(mén)的指令集用來(lái)處理 int 和 reference 類(lèi)型的條件分支比較操作,為了可以無(wú)需明顯標(biāo)識(shí)一個(gè)實(shí)體值是否 null,也有專(zhuān)門(mén)的指令用來(lái)檢測(cè) null 值。

boolean 類(lèi)型、byte 類(lèi)型、char 類(lèi)型和 short 類(lèi)型的條件分支比較操作,都使用 int 類(lèi)型的比較指令來(lái)完成,而對(duì)于 long 類(lèi)型、float 類(lèi)型和 double 類(lèi)型的條件分支比較操作,則會(huì)先執(zhí)行相應(yīng)類(lèi)型的比較運(yùn)算指令,運(yùn)算指令會(huì)返回一個(gè)整形值到操作數(shù)棧中,隨后再執(zhí)行 int 類(lèi)型的條件分支比較操作來(lái)完成整個(gè)分支跳轉(zhuǎn)。由于各種類(lèi)型的比較最終都會(huì)轉(zhuǎn)化為 int 類(lèi)型的比較操作,基于 int 類(lèi)型比較的這種重要性,Java 虛擬機(jī)提供了非常豐富的 int類(lèi)型的條件分支指令。

所有 int 類(lèi)型的條件分支轉(zhuǎn)移指令進(jìn)行的都是有符號(hào)的比較操作。

8、方法調(diào)用和返回指令

以下四條指令用于方法調(diào)用:

1、invokevirtual 指令用于調(diào)用對(duì)象的實(shí)例方法,根據(jù)對(duì)象的實(shí)際類(lèi)型進(jìn)行分派(虛方法分派),這也是 Java 語(yǔ)言中最常見(jiàn)的方法分派方式。

2、invokeinterface 指令用于調(diào)用接口方法,它會(huì)在運(yùn)行時(shí)搜索一個(gè)實(shí)現(xiàn)了這個(gè)接口方法的對(duì)象,找出適合的方法進(jìn)行調(diào)用。

3、invokespecial 指令用于調(diào)用一些需要特殊處理的實(shí)例方法,包括實(shí)例初始化方法、私有方法和父類(lèi)方法。

4、invokestatic 指令用于調(diào)用類(lèi)方法(static 方法)。

而方法返回指令則是根據(jù)返回值的類(lèi)型區(qū)分的,包括有 ireturn(當(dāng)返回值是 boolean、byte、char、short 和 int 類(lèi)型時(shí)使用)、lreturn、freturn、dreturn 和 areturn,另外還有一條 return 指令供聲明為 void 的方法、實(shí)例初始化方法、類(lèi)和接口的類(lèi)初始化方法使用。

9、拋出異常

在程序中顯式拋出異常的操作會(huì)由 athrow 指令實(shí)現(xiàn),除了這種情況,還有別的異常會(huì)在其它 Java 虛擬機(jī)指令檢測(cè)到異常狀況時(shí)由虛擬機(jī)自動(dòng)拋出。

10、同步

Java 虛擬機(jī)可以支持方法級(jí)的同步和方法內(nèi)部一段指令序列的同步,這兩種同步結(jié)構(gòu)都是使用管程(Monitor)來(lái)支持的。

方法級(jí)的同步是隱式,即無(wú)需通過(guò)字節(jié)碼指令來(lái)控制的,它實(shí)現(xiàn)在方法調(diào)用和返回操作之中。虛擬機(jī)可以從方法常量池中的方法表結(jié)構(gòu)(method_info Structure)中的 ACC_SYNCHRONIZED 訪問(wèn)標(biāo)志區(qū)分一個(gè)方法是否同步方法。當(dāng)方法調(diào)用時(shí),調(diào)用指令將會(huì)檢查方法的 ACC_SYNCHRONIZED 訪問(wèn)標(biāo)志是否被設(shè)置,如果設(shè)置了,執(zhí)行線程將先持有管程,然后再執(zhí)行方法,最后再方法完成(無(wú)論是正常完成還是非正常完成)時(shí)釋放管程。在方法執(zhí)行期間,執(zhí)行線程持有了管程,其他任何線程都無(wú)法再獲得同一個(gè)管程。如果一個(gè)同步方法執(zhí)行期間拋出了異常,并且在方法內(nèi)部無(wú)法處理此異常,那這個(gè)同步方法所持有的管程將在異常拋到同步方法之外時(shí)自動(dòng)釋放。

同步一段指令集序列通常是由 Java 語(yǔ)言中的 synchronized 塊來(lái)表示的,Java 虛擬機(jī)的指令集中有 monitorenter 和 monitorexit 兩條指令來(lái)支持 synchronized 關(guān)鍵字的語(yǔ)義,正確實(shí)現(xiàn) synchronized 關(guān)鍵字需要編譯器與 Java 虛擬機(jī)兩者協(xié)作支持。

結(jié)構(gòu)化鎖定(Structured Locking)是指在方法調(diào)用期間每一個(gè)管程退出都與前面的管程進(jìn)入相匹配的情形。因?yàn)闊o(wú)法保證所有提交給 Java 虛擬機(jī)執(zhí)行的代碼都滿足結(jié)構(gòu)化鎖定,所以 Java 虛擬機(jī)允許(但不強(qiáng)制要求)通過(guò)以下兩條規(guī)則來(lái)保證結(jié)構(gòu)化鎖定成立。假設(shè) T 代表一條線程,M 代表一個(gè)管程的話:

1、T 在方法執(zhí)行時(shí)持有管程 M 的次數(shù)必須與 T 在方法完成(包括正常和非正常完成)時(shí)釋放管程 M 的次數(shù)相等。

2、找方法調(diào)用過(guò)程中,任何時(shí)刻都不會(huì)出現(xiàn)線程 T 釋放管程 M 的次數(shù)比 T 持有管程 M 次數(shù)多的情況。

請(qǐng)注意,在同步方法調(diào)用時(shí)自動(dòng)持有和釋放管程的過(guò)程也被認(rèn)為是在方法調(diào)用期間發(fā)生。

11、棧和局部變量操作指令

(1)將常量壓入棧的指令

aconst_null將null對(duì)象引用壓入棧
iconst_m1 將int類(lèi)型常量-1壓入棧
iconst_0 將int類(lèi)型常量0壓入棧
iconst_1 將int類(lèi)型常量1壓入棧
iconst_2 將int類(lèi)型常量2壓入棧
iconst_3 將int類(lèi)型常量3壓入棧
iconst_4 將int類(lèi)型常量4壓入棧
iconst_5 將int類(lèi)型常量5壓入棧
lconst_0 將long類(lèi)型常量0壓入棧
lconst_1 將long類(lèi)型常量1壓入棧
fconst_0 將float類(lèi)型常量0壓入棧
fconst_1 將float類(lèi)型常量1壓入棧
dconst_0 將double類(lèi)型常量0壓入棧
dconst_1 將double類(lèi)型常量1壓入棧
bipush 將一個(gè)8位帶符號(hào)整數(shù)壓入棧
sipush 將16位帶符號(hào)整數(shù)壓入棧
ldc 把常量池中的項(xiàng)壓入棧
ldc_w 把常量池中的項(xiàng)壓入棧(使用寬索引)
ldc2_w 把常量池中l(wèi)ong類(lèi)型或者double類(lèi)型的項(xiàng)壓入棧(使用寬索引)

(2)從棧中的局部變量中裝載值的指令

iload 從局部變量中裝載int類(lèi)型值
lload 從局部變量中裝載long類(lèi)型值
fload 從局部變量中裝載float類(lèi)型值
dload 從局部變量中裝載double類(lèi)型值
aload 從局部變量中裝載引用類(lèi)型值(refernce)
iload_0 從局部變量0中裝載int類(lèi)型值
iload_1 從局部變量1中裝載int類(lèi)型值
iload_2 從局部變量2中裝載int類(lèi)型值
iload_3 從局部變量3中裝載int類(lèi)型值
lload_0 從局部變量0中裝載long類(lèi)型值
lload_1 從局部變量1中裝載long類(lèi)型值
lload_2 從局部變量2中裝載long類(lèi)型值

lload_3 從局部變量3中裝載long類(lèi)型值
fload_0 從局部變量0中裝載float類(lèi)型值
fload_1 從局部變量1中裝載float類(lèi)型值
fload_2 從局部變量2中裝載float類(lèi)型值
fload_3 從局部變量3中裝載float類(lèi)型值
dload_0 從局部變量0中裝載double類(lèi)型值
dload_1 從局部變量1中裝載double類(lèi)型值
dload_2 從局部變量2中裝載double類(lèi)型值
dload_3 從局部變量3中裝載double類(lèi)型值
aload_0 從局部變量0中裝載引用類(lèi)型值
aload_1 從局部變量1中裝載引用類(lèi)型值
aload_2 從局部變量2中裝載引用類(lèi)型值
aload_3 從局部變量3中裝載引用類(lèi)型值
iaload 從數(shù)組中裝載int類(lèi)型值
laload 從數(shù)組中裝載long類(lèi)型值
faload 從數(shù)組中裝載float類(lèi)型值
daload 從數(shù)組中裝載double類(lèi)型值
aaload 從數(shù)組中裝載引用類(lèi)型值
baload 從數(shù)組中裝載byte類(lèi)型或boolean類(lèi)型值
caload 從數(shù)組中裝載char類(lèi)型值
saload 從數(shù)組中裝載short類(lèi)型值

(3)將棧中的值存入局部變量的指令

istore 將int類(lèi)型值存入局部變量
lstore 將long類(lèi)型值存入局部變量
fstore 將float類(lèi)型值存入局部變量
dstore 將double類(lèi)型值存入局部變量
astore 將將引用類(lèi)型或returnAddress類(lèi)型值存入局部變量
istore_0 將int類(lèi)型值存入局部變量0
istore_1 將int類(lèi)型值存入局部變量1
istore_2 將int類(lèi)型值存入局部變量2

istore_3 將int類(lèi)型值存入局部變量3
lstore_0 將long類(lèi)型值存入局部變量0
lstore_1 將long類(lèi)型值存入局部變量1
lstore_2 將long類(lèi)型值存入局部變量2
lstore_3 將long類(lèi)型值存入局部變量3
fstore_0 將float類(lèi)型值存入局部變量0
fstore_1 將float類(lèi)型值存入局部變量1
fstore_2 將float類(lèi)型值存入局部變量2
fstore_3 將float類(lèi)型值存入局部變量3
dstore_0 將double類(lèi)型值存入局部變量0
dstore_1 將double類(lèi)型值存入局部變量1
dstore_2 將double類(lèi)型值存入局部變量2
dstore_3 將double類(lèi)型值存入局部變量3
astore_0 將引用類(lèi)型或returnAddress類(lèi)型值存入局部變量0
astore_1 將引用類(lèi)型或returnAddress類(lèi)型值存入局部變量1
astore_2 將引用類(lèi)型或returnAddress類(lèi)型值存入局部變量2
astore_3 將引用類(lèi)型或returnAddress類(lèi)型值存入局部變量3
iastore 將int類(lèi)型值存入數(shù)組中
lastore 將long類(lèi)型值存入數(shù)組中
fastore 將float類(lèi)型值存入數(shù)組中
dastore 將double類(lèi)型值存入數(shù)組中
aastore 將引用類(lèi)型值存入數(shù)組中
bastore 將byte類(lèi)型或者boolean類(lèi)型值存入數(shù)組中
castore 將char類(lèi)型值存入數(shù)組中
sastore 將short類(lèi)型值存入數(shù)組中
wide指令
wide 使用附加字節(jié)擴(kuò)展局部變量索引

(4)通用(無(wú)類(lèi)型)棧操作

nop 不做任何操作
pop 彈出棧頂端一個(gè)字長(zhǎng)的內(nèi)容
pop2 彈出棧頂端兩個(gè)字長(zhǎng)的內(nèi)容
dup 復(fù)制棧頂部一個(gè)字長(zhǎng)內(nèi)容
dup_x1 復(fù)制棧頂部一個(gè)字長(zhǎng)的內(nèi)容,然后將復(fù)制內(nèi)容及原來(lái)彈出的兩個(gè)字長(zhǎng)的內(nèi)容壓入棧

dup_x2 復(fù)制棧頂部一個(gè)字長(zhǎng)的內(nèi)容,然后將復(fù)制內(nèi)容及原來(lái)彈出的三個(gè)字長(zhǎng)的內(nèi)容壓入棧
dup2 復(fù)制棧頂部?jī)蓚€(gè)字長(zhǎng)內(nèi)容
dup2_x1 復(fù)制棧頂部?jī)蓚€(gè)字長(zhǎng)的內(nèi)容,然后將復(fù)制內(nèi)容及原來(lái)彈出的三個(gè)字長(zhǎng)的內(nèi)容壓入棧
dup2_x2 復(fù)制棧頂部?jī)蓚€(gè)字長(zhǎng)的內(nèi)容,然后將復(fù)制內(nèi)容及原來(lái)彈出的四個(gè)字長(zhǎng)的內(nèi)容壓入棧
swap 交換棧頂部?jī)蓚€(gè)字長(zhǎng)內(nèi)容

(5)類(lèi)型轉(zhuǎn)換

i2l 把int類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為long類(lèi)型
i2f 把int類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為float類(lèi)型
i2d 把int類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為double類(lèi)型
l2i 把long類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為int類(lèi)型
l2f 把long類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為float類(lèi)型
l2d 把long類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為double類(lèi)型
f2i 把float類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為int類(lèi)型
f2l 把float類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為long類(lèi)型
f2d 把float類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為double類(lèi)型
d2i 把double類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為int類(lèi)型
d2l 把double類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為long類(lèi)型
d2f 把double類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為float類(lèi)型
i2b 把int類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為byte類(lèi)型
i2c 把int類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為char類(lèi)型
i2s 把int類(lèi)型的數(shù)據(jù)轉(zhuǎn)化為short類(lèi)型

(6)整數(shù)運(yùn)算

iadd 執(zhí)行int類(lèi)型的加法
ladd 執(zhí)行l(wèi)ong類(lèi)型的加法
isub 執(zhí)行int類(lèi)型的減法
lsub 執(zhí)行l(wèi)ong類(lèi)型的減法
imul 執(zhí)行int類(lèi)型的乘法
lmul 執(zhí)行l(wèi)ong類(lèi)型的乘法
idiv 執(zhí)行int類(lèi)型的除法
ldiv 執(zhí)行l(wèi)ong類(lèi)型的除法

irem 計(jì)算int類(lèi)型除法的余數(shù)
lrem 計(jì)算long類(lèi)型除法的余數(shù)
ineg 對(duì)一個(gè)int類(lèi)型值進(jìn)行取反操作
lneg 對(duì)一個(gè)long類(lèi)型值進(jìn)行取反操作
iinc 把一個(gè)常量值加到一個(gè)int類(lèi)型的局部變量上

(7)邏輯運(yùn)算

移位操作

ishl 執(zhí)行int類(lèi)型的向左移位操作
lshl 執(zhí)行l(wèi)ong類(lèi)型的向左移位操作
ishr 執(zhí)行int類(lèi)型的向右移位操作
lshr 執(zhí)行l(wèi)ong類(lèi)型的向右移位操作
iushr 執(zhí)行int類(lèi)型的向右邏輯移位操作
lushr 執(zhí)行l(wèi)ong類(lèi)型的向右邏輯移位操作

按位布爾運(yùn)算

iand 對(duì)int類(lèi)型值進(jìn)行“邏輯與”操作
land 對(duì)long類(lèi)型值進(jìn)行“邏輯與”操作
ior 對(duì)int類(lèi)型值進(jìn)行“邏輯或”操作
lor 對(duì)long類(lèi)型值進(jìn)行“邏輯或”操作
ixor 對(duì)int類(lèi)型值進(jìn)行“邏輯異或”操作
lxor 對(duì)long類(lèi)型值進(jìn)行“邏輯異或”操作

浮點(diǎn)運(yùn)算

fadd 執(zhí)行float類(lèi)型的加法
dadd 執(zhí)行double類(lèi)型的加法
fsub 執(zhí)行float類(lèi)型的減法
dsub 執(zhí)行double類(lèi)型的減法
fmul 執(zhí)行float類(lèi)型的乘法
dmul 執(zhí)行double類(lèi)型的乘法
fdiv 執(zhí)行float類(lèi)型的除法
ddiv 執(zhí)行double類(lèi)型的除法
frem 計(jì)算float類(lèi)型除法的余數(shù)
drem 計(jì)算double類(lèi)型除法的余數(shù)
fneg 將一個(gè)float類(lèi)型的數(shù)值取反
dneg 將一個(gè)double類(lèi)型的數(shù)值取反

(8)對(duì)象和數(shù)組

對(duì)象操作指令

new 創(chuàng)建一個(gè)新對(duì)象
checkcast 確定對(duì)象為所給定的類(lèi)型
getfield 從對(duì)象中獲取字段
putfield 設(shè)置對(duì)象中字段的值
getstatic 從類(lèi)中獲取靜態(tài)字段
putstatic 設(shè)置類(lèi)中靜態(tài)字段的值
instanceof 判斷對(duì)象是否為給定的類(lèi)型

數(shù)組操作指令

newarray 分配數(shù)據(jù)成員類(lèi)型為基本上數(shù)據(jù)類(lèi)型的新數(shù)組
anewarray 分配數(shù)據(jù)成員類(lèi)型為引用類(lèi)型的新數(shù)組
arraylength 獲取數(shù)組長(zhǎng)度
multianewarray 分配新的多維數(shù)組

(9)控制流

條件分支指令

ifeq 如果等于0,則跳轉(zhuǎn)
ifne 如果不等于0,則跳轉(zhuǎn)
iflt 如果小于0,則跳轉(zhuǎn)
ifge 如果大于等于0,則跳轉(zhuǎn)
ifgt 如果大于0,則跳轉(zhuǎn)
ifle 如果小于等于0,則跳轉(zhuǎn)
if_icmpcq 如果兩個(gè)int值相等,則跳轉(zhuǎn)
if_icmpne 如果兩個(gè)int類(lèi)型值不相等,則跳轉(zhuǎn)
if_icmplt 如果一個(gè)int類(lèi)型值小于另外一個(gè)int類(lèi)型值,則跳轉(zhuǎn)
if_icmpge 如果一個(gè)int類(lèi)型值大于或者等于另外一個(gè)int類(lèi)型值,則跳轉(zhuǎn)
if_icmpgt 如果一個(gè)int類(lèi)型值大于另外一個(gè)int類(lèi)型值,則跳轉(zhuǎn)
if_icmple 如果一個(gè)int類(lèi)型值小于或者等于另外一個(gè)int類(lèi)型值,則跳轉(zhuǎn)
ifnull 如果等于null,則跳轉(zhuǎn)
ifnonnull 如果不等于null,則跳轉(zhuǎn)
if_acmpeq 如果兩個(gè)對(duì)象引用相等,則跳轉(zhuǎn)
if_acmpnc 如果兩個(gè)對(duì)象引用不相等,則跳轉(zhuǎn)

比較指令

lcmp 比較long類(lèi)型值
fcmpl 比較float類(lèi)型值(當(dāng)遇到NaN時(shí),返回-1)
fcmpg 比較float類(lèi)型值(當(dāng)遇到NaN時(shí),返回1)
dcmpl 比較double類(lèi)型值(當(dāng)遇到NaN時(shí),返回-1)
dcmpg 比較double類(lèi)型值(當(dāng)遇到NaN時(shí),返回1)

無(wú)條件轉(zhuǎn)移指令

goto 無(wú)條件跳轉(zhuǎn)
goto_w 無(wú)條件跳轉(zhuǎn)(寬索引)

表跳轉(zhuǎn)指令

tableswitch 通過(guò)索引訪問(wèn)跳轉(zhuǎn)表,并跳轉(zhuǎn)
lookupswitch 通過(guò)鍵值匹配訪問(wèn)跳轉(zhuǎn)表,并執(zhí)行跳轉(zhuǎn)操作

異常

athrow 拋出異常或錯(cuò)誤
finally子句
jsr 跳轉(zhuǎn)到子例程
jsr_w 跳轉(zhuǎn)到子例程(寬索引)
rct 從子例程返回

(10)方法調(diào)用與返回

方法調(diào)用指令

invokcvirtual 運(yùn)行時(shí)按照對(duì)象的類(lèi)來(lái)調(diào)用實(shí)例方法
invokespecial 根據(jù)編譯時(shí)類(lèi)型來(lái)調(diào)用實(shí)例方法
invokestatic 調(diào)用類(lèi)(靜態(tài))方法
invokcinterface 調(diào)用接口方法

方法返回指令

ireturn 從方法中返回int類(lèi)型的數(shù)據(jù)
lreturn 從方法中返回long類(lèi)型的數(shù)據(jù)
freturn 從方法中返回float類(lèi)型的數(shù)據(jù)
dreturn 從方法中返回double類(lèi)型的數(shù)據(jù)
areturn 從方法中返回引用類(lèi)型的數(shù)據(jù)
return 從方法中返回,返回值為void

線程同步

montiorenter 進(jìn)入并獲取對(duì)象監(jiān)視器
monitorexit 釋放并退出對(duì)象監(jiān)視器

總結(jié)

以上是生活随笔為你收集整理的【深入理解JVM】JVM字节码指令集的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。