日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

JVM字節(jié)碼指令集簡介

 gaoshenmu 2016-09-11

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

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

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

操作數(shù)的數(shù)量以及長度取決于操作碼,如果一個操作數(shù)的長度超過了一個字節(jié),那它將會以 Big-Endian 順序存儲——即高位在前的字節(jié)序。舉個例子,如果要將一個 16 位長度的無符號整數(shù)使用兩個無符號字節(jié)存儲起來(將它們命名為 byte1 和 byte2),那它們的值應該是這樣的:

(byte18)|byte2
  • 1

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

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

數(shù)據(jù)類型與Java虛擬機

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

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

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

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

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

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

這里寫圖片描述

在 Java 虛擬機中,實際類型與運算類型之間的映射關系,如下表所示:

這里寫圖片描述

有部分對操作棧進行操作的 Java 虛擬機指令(例如 pop 和 swap 指令)是與具體類型無關的,不過這些指令也必須受到運算類型分類的限制,這些分類也在表中列出了。

加載和存儲指令

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

1、將一個局部變量加載到操作棧的指令包括有:iload、iload_n>、lload、lload_n>、fload、fload_n>、dload、dload_n>、aload、aload_n>2、將一個數(shù)值從操作數(shù)棧存儲到局部變量表的指令包括有:istore、istore_n>、lstore、lstore_n>、fstore、fstore_n>、dstore、dstore_n>、astore、astore_n>3、將一個常量加載到操作數(shù)棧的指令包括有:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_i>、lconst_l>、fconst_f>、dconst_d>4、擴充局部變量表的訪問索引的指令:wide
  • 1
  • 2
  • 3
  • 4

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

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

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

運算指令

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

整數(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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

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

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

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

在把浮點數(shù)轉(zhuǎn)換為整數(shù)時,Java 虛擬機使用 IEEE 754 標準中的向零舍入模式,這種模式的舍入結果會導致數(shù)字被截斷,所有小數(shù)部分的有效字節(jié)都會被丟棄掉。向零舍入模式將在目標數(shù)值類型中選擇一個最接近,但是不大于原值的數(shù)字來作為最精確的舍入結果。

Java 虛擬機在處理浮點數(shù)運算時,不會拋出任何運行時異常(這里所講的是 Java 的異常,請勿與 IEEE 754 規(guī)范中的浮點異常互相混淆),當一個操作產(chǎn)生溢出時,將會使用有符號的無窮大來表示,如果某個操作結果沒有明確的數(shù)學定義的話,將會時候 NaN 值來表示。所有使用 NaN 值作為操作數(shù)的算術操作,結果都會返回 NaN。

在對 long 類型數(shù)值進行比較時,虛擬機采用帶符號的比較方式,而對浮點數(shù)值進行比較時(dcmpg、dcmpl、fcmpg、fcmpl),虛擬機采用 IEEE 754 規(guī)范說定義的無信號比較(Nonsignaling Comparisons)方式。

類型轉(zhuǎn)換指令

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

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

int 類型到 long、float 或者 double 類型 long 類型到 float、double 類型 float 類型到 double 類型
  • 1
  • 2
  • 3
  • 4

窄化類型轉(zhuǎn)換(Narrowing Numeric Conversions)指令包括有:i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l 和 d2f。窄化類型轉(zhuǎn)換可能會導致轉(zhuǎn)換結果產(chǎn)生不同的正負號、不同的數(shù)量級,轉(zhuǎn)換過程很可能會導致數(shù)值丟失精度。

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

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

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

如果 T 是 long 類型,并且轉(zhuǎn)換結果在 long 類型的表示范圍之內(nèi),那就轉(zhuǎn)換為 long 類型數(shù)值 v 如果 T 是 int 類型,并且轉(zhuǎn)換結果在 int 類型的表示范圍之內(nèi),那就轉(zhuǎn)換為 int 類型數(shù)值 v否則: 如果轉(zhuǎn)換結果 v 的值太小(包括足夠小的負數(shù)以及負無窮大的情況),無法使用 T 類 型表示的話,那轉(zhuǎn)換結果取 int 或 long 類型所能表示的最小數(shù)字。 如果轉(zhuǎn)換結果 v 的值太大(包括足夠大的正數(shù)以及正無窮大的情況),無法使用 T 類 型表示的話,那轉(zhuǎn)換結果取 int 或 long 類型所能表示的最大數(shù)字。

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

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

對象創(chuàng)建與操作

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

1、創(chuàng)建類實例的指令:new

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

3、訪問類字段(static 字段,或者稱為類變量)和實例字段(非 static 字段,或者成為實例變量)的指令:getfield、putfield、getstatic、putstatic

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

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

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

7、檢查類實例類型的指令:instanceof、checkcas

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

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

控制轉(zhuǎn)移指令

控制轉(zhuǎn)移指令可以讓 Java 虛擬機有條件或無條件地從指定指令而不是控制轉(zhuǎn)移指令的下一條指令繼續(xù)執(zhí)行程序??刂妻D(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、復合條件分支:tableswitch、lookupswitch

3、無條件分支:goto、goto_w、jsr、jsr_w、ret

在 Java 虛擬機中有專門的指令集用來處理 int 和 reference 類型的條件分支比較操作,為了可以無需明顯標識一個實體值是否 null,也有專門的指令用來檢測 null 值。

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

所有 int 類型的條件分支轉(zhuǎn)移指令進行的都是有符號的比較操作。

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

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

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

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

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

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

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

拋出異常

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

同步

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

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

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

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

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

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

請注意,在同步方法調(diào)用時自動持有和釋放管程的過程也被認為是在方法調(diào)用期間發(fā)生。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多