歡迎來到“Under The Hood”第五期。本期我們來看看JVM中處理對象和數(shù)組的字節(jié)碼。你可能需要閱讀往期的文章才能更好的理解本文。
面向?qū)ο蟮臋C器
JVM中的數(shù)據(jù)有3種形式:對象(object),對象引用(object reference)和原始類型(primitive type)。對象存放在垃圾收集堆中;對象引用和原始類型,根據(jù)它們作用域范圍的不同,分別存放在不同的地方:作為本地變量,存放在Java棧中;作為實例變量,存放在垃圾收集堆上;作為類變量,存放在方法區(qū)上。
在JVM中,垃圾收集堆上只能給對象分配內(nèi)存空間。原始類型,除了作為對象的一部分,沒有其他方式可以給它在堆上分配空間。在需要對象引用的地方,如果你想使用原始類型,你可以給原始類型分配java.lang包中相應(yīng)的包裝對象。只有對象引用和原始類型可以作為本地變量,存放在Java棧中,對象是不可能存放在棧中的。
JVM中,對象和原始類型在架構(gòu)上的分離,體現(xiàn)在Java編程語言中就是:對象不能被聲明成本地變量,只有對象引用才行。在聲明時,對象引用不會指向具體對象,只有引用被顯式的初始化之后(指向已存在對象,或者通過new關(guān)鍵字創(chuàng)建新對象),引用才會指向?qū)嶋H對象。
在JVM指令集中,除了數(shù)組,所有的對象都通過相同的操作符集來實例化和訪問。在Java中,數(shù)組也是對象,并且就像Java程序中任何其他對象一樣,是動態(tài)創(chuàng)建的。數(shù)組引用可以用在任何需要Object類型的引用的地方,Object中的任何方法,都可以在數(shù)組上調(diào)用。但是,在JVM里,數(shù)組是使用有別于對象的特殊字節(jié)碼來處理的。
就像任何其他對象一樣,數(shù)組不能被聲明為本地變量;只有數(shù)組引用可以。數(shù)組對象本身,總是包含一組原始類型或者一組對象引用。如果你聲明一個對象數(shù)組,你會得到一組對象引用。對象們自己必須用new顯式創(chuàng)建,并被賦值給數(shù)組的元素。
處理對象的字節(jié)碼
實例化新對象是通過操作碼new來實現(xiàn)的,它需要2個單字節(jié)的操作數(shù)。這2個單字節(jié)操作數(shù)合并成16位的常量池索引。常量池中對應(yīng)的元素給出了新對象的類型信息。就像下面所展示的,JVM在堆上創(chuàng)建新的對象實例,并把它的引用壓入棧中。
OPCODE
|
OPERAND(S)
|
DESCRIPTION
|
new |
indexbyte1, indexbyte2 |
creates a new object on the heap, pushes reference |
接下來的表格,列出了存取對象字段(field)的字節(jié)碼。操作符putfield和getfield只負(fù)責(zé)處理實例變量。靜態(tài)變量使用putstatic和getstatic訪問,這個我們待會再說。操作符putfield和getfield都有2個單字節(jié)操作數(shù),它們合并成16位的常量池索引。相應(yīng)的常量池位置上存放著關(guān)于字段的類型,大小和偏移量的信息。putfield和getfield都會從棧中取得操作對象的引用。putfield從棧上獲取所要賦給實例變量的值,而getfield則把取到的實例變量的值壓進(jìn)棧中。
OPCODE
|
OPERAND(S)
|
DESCRIPTION
|
putfield |
indexbyte1, indexbyte2 |
set field, indicated by index of object to value (both taken from stack) |
getfield |
indexbyte1, indexbyte2 |
pushes field, indicated by index of object (taken from stack) |
如下表所示,靜態(tài)變量通過putstatic和getstatic來訪問。它們所擁有的2個單字節(jié)的操作數(shù),會由JVM合并成16位的常量池索引。對應(yīng)的常量池位置上存有靜態(tài)變量的相關(guān)信息。由于靜態(tài)變量不跟任何對象關(guān)聯(lián),putstatic和getstatic也不會使用對象引用。putstatic從棧中取得所要附給靜態(tài)變量的值,而getstatic則把靜態(tài)變量的值壓入棧中。
OPCODE
|
OPERAND(S)
|
DESCRIPTION
|
putstatic |
indexbyte1, indexbyte2 |
set field, indicated by index, of object to value (both taken from stack) |
getstatic |
indexbyte1, indexbyte2 |
pushes field, indicated by index, of object (taken from stack) |
下面的操作碼用來檢查棧頂?shù)膶ο笠茫遣皇侵赶蛴刹僮鲾?shù)所索引的類或接口的實例。
當(dāng)對象不是指定類或接口的實例時,checkcast指令會拋出CheckCastException異常;反之,checkcast什么也不做,對象引用仍保留在棧頂,繼續(xù)執(zhí)行下一個指令。checkcast指令保證了運行時的類型轉(zhuǎn)換是安全的,它是JVM安全系統(tǒng)的一部分。
instanceof指令彈出棧頂?shù)膶ο笠茫詈蟀裻rue或false壓入棧中。如果對象確實是指定類或接口的實例,則把true入棧;反之,false入棧。instanceof指令實現(xiàn)了Java語言中的instanceof關(guān)鍵字,它允許程序員測試一個對象是不是某個類或接口的實例。
OPCODE
|
OPERAND(S)
|
DESCRIPTION
|
checkcast |
indexbyte1, indexbyte2 |
Throws ClassCastException if objectref on stack cannot be cast to class at index |
instanceof |
indexbyte1, indexbyte2 |
Pushes true if objectref on stack is an instanceof class at index, else pushes false |
處理數(shù)組的字節(jié)碼
可以通過指令newarray,anewarray,和multianewarray實例化數(shù)組。
newarray用來創(chuàng)建原始類型的數(shù)組,具體類型由它的單字節(jié)操作數(shù)指定。它可以創(chuàng)建的數(shù)組類型有byte,short,char,int,long,float,double和boolean。
anewarray創(chuàng)建對象引用數(shù)組,它的2個單字節(jié)操作數(shù)合并成16位常量池索引,由此獲取到要創(chuàng)建的包含在數(shù)組中的對象類型信息。anewarray為數(shù)組中的對象引用分配控件,并把它們都設(shè)置為null。
multianewarray用來分配多維數(shù)組(數(shù)組的數(shù)組),可以通過重復(fù)調(diào)用newarray和anewarray來實現(xiàn)。multianewarray指令只是簡單的把創(chuàng)建多維數(shù)組的多條字節(jié)碼壓縮成一個指令。它的開頭2個單字節(jié)操作數(shù)合并成常量池索引,由此獲取多維數(shù)組中的對象類型。第3個單字節(jié)操作數(shù)指明了多維數(shù)組的維數(shù)。至于每維的數(shù)組大小需從棧上獲取。該指令為多維數(shù)組中所有的數(shù)組分配空間。
OPCODE
|
OPERAND(S)
|
DESCRIPTION
|
newarray |
atype |
pops length, allocates new array of primitive types of type indicated by atype, pushes objectref of new array |
anewarray |
indexbyte1, indexbyte2 |
pops length, allocates a new array of objects of class indicated by indexbyte1 and indexbyte2, pushes objectref of new array |
multianewarray |
indexbyte1, indexbyte2, dimensions |
pops dimensions number of array lengths, allocates a new multidimensional array of class indicated by indexbyte1 and indexbyte2, pushes objectref of new array |
下表列出的指令把棧頂?shù)臄?shù)組引用彈出,把它的長度壓入棧中。
OPCODE
|
OPERAND(S)
|
DESCRIPTION
|
arraylength |
(none) |
pops objectref of an array, pushes length of that array |
下面的操作碼獲取數(shù)組中的元素。數(shù)組索引和數(shù)組引用從棧上彈出,數(shù)組中指定索引處的值被壓入棧中。
OPCODE
|
OPERAND(S)
|
DESCRIPTION
|
baload |
(none) |
pops index and arrayref of an array of bytes, pushes arrayref[index] |
caload |
(none) |
pops index and arrayref of an array of chars, pushes arrayref[index] |
saload |
(none) |
pops index and arrayref of an array of shorts, pushes arrayref[index] |
iaload |
(none) |
pops index and arrayref of an array of ints, pushes arrayref[index] |
laload |
(none) |
pops index and arrayref of an array of longs, pushes arrayref[index] |
faload |
(none) |
pops index and arrayref of an array of floats, pushes arrayref[index] |
daload |
(none) |
pops index and arrayref of an array of doubles, pushes arrayref[index] |
aaload |
(none) |
pops index and arrayref of an array of objectrefs, pushes arrayref[index] |
下表列出了把值存入數(shù)組元素中的操作碼。值、索引和數(shù)組引用均從棧頂彈出。
OPCODE
|
OPERAND(S)
|
DESCRIPTION
|
bastore |
(none) |
pops value index, and arrayref of an array of bytes, assigns arrayref[index] = value |
castore |
(none) |
pops value index, and arrayref of an array of chars, assigns arrayref[index] = value |
sastore |
(none) |
pops value index, and arrayref of an array of shorts, assigns arrayref[index] = value |
iastore |
(none) |
pops value index, and arrayref of an array of ints ,assigns arrayref[index] = value |
lastore |
(none) |
pops value index, and arrayref of an array of longs, assigns arrayref[index] = value |
fastore |
(none) |
pops value index, and arrayref of an array of floats, assigns arrayref[index] = value |
dastore |
(none) |
pops value index, and arrayref of an array of doubles, assigns arrayref[index] = value |
aastore |
(none) |
pops value index, and arrayref of an array of objectrefs, assigns arrayref[index] = value |
本文譯自:Objects and Arrays