物件和陣列:JVM中,處理物件和陣列的位元組碼介紹
轉:http://letscoding.iteye.com/blog/2073759?utm_source=tuicool&utm_medium=referral
譯:http://www.javaworld.com/article/2077305/learn-java/objects-and-arrays.html
歡迎來到“Under The Hood”第五期。本期我們來看看JVM中處理物件和陣列的位元組碼。你可能需要閱讀往期的文章才能更好的理解本文。
面向物件的機器
JVM中的資料有3種形式:物件(object),物件引用(object reference)和原始型別(primitive type)。物件存放在垃圾收集堆中;物件引用和原始型別,根據它們作用域範圍的不同,分別存放在不同的地方:作為本地變數,存放在Java棧中;作為例項變數,存放在垃圾收集堆上;作為類變數,存放在方法區上。
在JVM中,垃圾收集堆上只能給物件分配記憶體空間。原始型別,除了作為物件的一部分,沒有其他方式可以給它在堆上分配空間。在需要物件引用的地方,如果你想使用原始型別,你可以給原始型別分配java.lang包中相應的包裝物件。只有物件引用和原始型別可以作為本地變數,存放在Java棧中,物件是不可能存放在棧中的。
JVM中,物件和原始型別在架構上的分離,體現在Java程式語言中就是:物件不能被宣告成本地變數,只有物件引用才行。在宣告時,物件引用不會指向具體物件,只有引用被顯式的初始化之後(指向已存在物件,或者通過new關鍵字建立新物件),引用才會指向實際物件。
在JVM指令集中,除了陣列,所有的物件都通過相同的操作符集來例項化和訪問。在Java中,陣列也是物件,並且就像Java程式中任何其他物件一樣,是動態建立的。陣列引用可以用在任何需要Object型別的引用的地方,Object中的任何方法,都可以在陣列上呼叫。但是,在JVM裡,陣列是使用有別於物件的特殊位元組碼來處理的。
就像任何其他物件一樣,陣列不能被宣告為本地變數;只有陣列引用可以。陣列物件本身,總是包含一組原始型別或者一組物件引用。如果你宣告一個物件陣列,你會得到一組物件引用。物件們自己必須用new顯式建立,並被賦值給陣列的元素。
處理物件的位元組碼
例項化新物件是通過操作碼new來實現的,它需要2個單位元組的運算元。這2個單位元組運算元合併成16位的常量池索引。常量池中對應的元素給出了新物件的型別資訊。就像下面所展示的,JVM在堆上建立新的物件例項,並把它的引用壓入棧中。
new | indexbyte1, indexbyte2 | creates a new object on the heap, pushes reference |
接下來的表格,列出了存取物件欄位(field)的位元組碼。操作符putfield和getfield只負責處理例項變數。靜態變數使用putstatic和getstatic訪問,這個我們待會再說。操作符putfield和getfield都有2個單位元組運算元,它們合併成16位的常量池索引。相應的常量池位置上存放著關於欄位的型別,大小和偏移量的資訊。putfield和getfield都會從棧中取得操作物件的引用。putfield從棧上獲取所要賦給例項變數的值,而getfield則把取到的例項變數的值壓進棧中。
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) |
如下表所示,靜態變數通過putstatic和getstatic來訪問。它們所擁有的2個單位元組的運算元,會由JVM合併成16位的常量池索引。對應的常量池位置上存有靜態變數的相關資訊。由於靜態變數不跟任何物件關聯,putstatic和getstatic也不會使用物件引用。putstatic從棧中取得所要附給靜態變數的值,而getstatic則把靜態變數的值壓入棧中。
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) |
下面的操作碼用來檢查棧頂的物件引用,是不是指向由運算元所索引的類或介面的例項。
當物件不是指定類或介面的例項時,checkcast指令會丟擲CheckCastException異常;反之,checkcast什麼也不做,物件引用仍保留在棧頂,繼續執行下一個指令。checkcast指令保證了執行時的型別轉換是安全的,它是JVM安全系統的一部分。
instanceof指令彈出棧頂的物件引用,最後把true或false壓入棧中。如果物件確實是指定類或介面的例項,則把true入棧;反之,false入棧。instanceof指令實現了Java語言中的instanceof關鍵字,它允許程式設計師測試一個物件是不是某個類或介面的例項。
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 |
處理陣列的位元組碼
可以通過指令newarray,anewarray,和multianewarray例項化陣列。
newarray用來建立原始型別的陣列,具體型別由它的單位元組運算元指定。它可以建立的陣列型別有byte,short,char,int,long,float,double和boolean。
anewarray建立物件引用陣列,它的2個單位元組運算元合併成16位常量池索引,由此獲取到要建立的包含在陣列中的物件型別資訊。anewarray為陣列中的物件引用分配控制元件,並把它們都設定為null。
multianewarray用來分配多維陣列(陣列的陣列),可以通過重複呼叫newarray和anewarray來實現。multianewarray指令只是簡單的把建立多維陣列的多條位元組碼壓縮成一個指令。它的開頭2個單位元組運算元合併成常量池索引,由此獲取多維陣列中的物件型別。第3個單位元組運算元指明瞭多維陣列的維數。至於每維的陣列大小需從棧上獲取。該指令為多維陣列中所有的陣列分配空間。
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 |
下表列出的指令把棧頂的陣列引用彈出,把它的長度壓入棧中。
arraylength | (none) | pops objectref of an array, pushes length of that array |
下面的操作碼獲取陣列中的元素。陣列索引和陣列引用從棧上彈出,陣列中指定索引處的值被壓入棧中。
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] |
下表列出了把值存入陣列元素中的操作碼。值、索引和陣列引用均從棧頂彈出。
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 |