1. 程式人生 > >物件和陣列:JVM中,處理物件和陣列的位元組碼介紹

物件和陣列: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