原創(chuàng)|其它|編輯:郝浩|2009-03-06 16:04:47.000|閱讀 483 次
概述:通過(guò)上一篇文章的介紹我們了解了JVM中數(shù)據(jù)類型以及數(shù)據(jù)區(qū)的知識(shí),這篇我們會(huì)通過(guò)對(duì)JVM堆棧的幀的詳細(xì)介紹了解方法執(zhí)行的一些內(nèi)幕。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
通過(guò)上一篇文章的介紹我們了解了JVM中數(shù)據(jù)類型以及數(shù)據(jù)區(qū)的知識(shí),這篇我們會(huì)通過(guò)對(duì)JVM堆棧的幀的詳細(xì)介紹了解方法執(zhí)行的一些內(nèi)幕。
幀通常用于存儲(chǔ)數(shù)據(jù)和部分結(jié)果,同時(shí)還用于執(zhí)行動(dòng)態(tài)鏈接、返回方法的返回值以及分發(fā)異常。
幀在方法調(diào)用的時(shí)候被創(chuàng)建,在方法完成的時(shí)候銷毀。它是在創(chuàng)建它的線程的JVM堆棧中分配到空間的,每個(gè)幀都有它自己的局部變量數(shù)組、操作數(shù)堆棧和一個(gè)當(dāng)前方法所在的類的運(yùn)行時(shí)常量池的引用。
它的局部變量數(shù)組和操作數(shù)堆棧的大小是在編譯的時(shí)候就確定了的,而且它是和它所聯(lián)系的方法的代碼一起提供的,因此它的數(shù)據(jù)結(jié)構(gòu)的尺寸僅僅依賴于JVM的實(shí)現(xiàn)和方法調(diào)用時(shí)同時(shí)可以分配的內(nèi)存。
對(duì)于正在執(zhí)行的方法而言只有一個(gè)幀是活動(dòng)的,這個(gè)幀就是所謂的當(dāng)前幀,它的方法就是當(dāng)前方法,當(dāng)前方法所在的類被定義為當(dāng)前類。局部變量和操作數(shù)堆棧的操作通常和當(dāng)前幀有關(guān)。
如果一個(gè)幀所在的方法調(diào)用了另外的方法或者方法結(jié)束,那么該幀不再是當(dāng)前幀。如果是調(diào)用另外的方法,那么一個(gè)新的幀會(huì)被創(chuàng)建并且在控制權(quán)轉(zhuǎn)換到新方法時(shí)成為當(dāng)前幀;如果是方法結(jié)束,如果有方法返回,當(dāng)前幀將它的方法調(diào)用的結(jié)果傳遞給前一個(gè)幀,當(dāng)前一個(gè)幀成為當(dāng)前幀時(shí)當(dāng)前幀被丟棄。
需要注意的是由一個(gè)線程創(chuàng)建的幀是局部于該線程的,其它的線程不能引用它。
每個(gè)幀都包含變量數(shù)組,也就是我們所熟知的局部變量數(shù)組。一個(gè)局部變量可以保存一個(gè)boolean、 byte、char、short、int、float、引用或者returnAddress值,一對(duì)局部變量才能保存一個(gè)long或者double值。
局部變量是根據(jù)索引進(jìn)行尋址的,第一個(gè)局部變量的索引是0。如果一個(gè)整型值介于0和局部變量數(shù)組的長(zhǎng)度之間并且也只有在這個(gè)區(qū)間的時(shí)候它才會(huì)被作為局部變量數(shù)組的索引。
long型或者double型的值占用兩個(gè)連續(xù)的局部變量,這樣的值可能只能使用較小的那個(gè)索引值進(jìn)行尋址,例如,局部變量數(shù)組中索引為n的 double變量值實(shí)際上占用n和n+1,但是局部變量n+1是不能讀取的,它可以被寫入,但是這樣做會(huì)使得局部變量n的內(nèi)容無(wú)效。JVM沒(méi)有要求n是偶數(shù),這就意味著double和long型值在局部變量數(shù)組中不必是64位對(duì)齊的,JVM的實(shí)現(xiàn)者可以決定使用適當(dāng)?shù)姆绞奖硎灸菢拥闹怠?/p>
JVM使用局部變量傳遞方法調(diào)用的參數(shù),對(duì)于類方法調(diào)用(也就是static方法),所有的參數(shù)都是連續(xù)的存儲(chǔ)在局部變量表中并且是從0開(kāi)始的,對(duì)于實(shí)例方法調(diào)用,所有的參數(shù)也是連續(xù)的但是是從1開(kāi)始的,局部變量0存儲(chǔ)的是實(shí)例方法所在的類實(shí)例的引用。
每個(gè)幀都包含一個(gè)后進(jìn)先出的堆棧,也就是它的操作數(shù)堆棧。
操作數(shù)堆棧在剛剛被創(chuàng)建的時(shí)候是空的,JVM提供指令從局部變量或者成員加載常量或者值到堆棧,其它的JVM指令從操作數(shù)堆棧提取操作數(shù),操作它們并將結(jié)果放回操作數(shù)堆棧。操作數(shù)堆棧也用于準(zhǔn)備傳遞給方法的參數(shù)以及接收方法的結(jié)果。
例如一個(gè)iadd指令將兩個(gè)int值相加,該指令要求它的前一條指令將它要相加的兩個(gè)值壓入操作數(shù)堆棧的最上面,它從操作數(shù)堆棧取出那兩個(gè)值進(jìn)行相加并將結(jié)果放回操作數(shù)堆棧。
子計(jì)算可能是嵌套在操作數(shù)堆棧中的,產(chǎn)生的值可以被嵌入的計(jì)算使用。
操作數(shù)堆棧的每一項(xiàng)都可以保存JVM的任何類型的值,包括long和double型的。
操作數(shù)堆棧中的值必須根據(jù)其類型進(jìn)行操作。下面的這些情況都是不可能的:壓入兩個(gè)int值而后續(xù)的操作將它們作為long型或者壓入兩個(gè)float值而后續(xù)的操作是iadd指令(該指令的操作對(duì)象是兩個(gè)int型)。有一小部分JVM指令(例如dup和swap)將運(yùn)行時(shí)數(shù)據(jù)區(qū)的值作為原始的值(raw value)進(jìn)行操作而不考慮其類型,這些指令是以一種不能用于修改或者分解單獨(dú)的值的方式定義的,這些對(duì)操作數(shù)堆棧操作的限制通過(guò)類文件驗(yàn)證進(jìn)行了強(qiáng)制。
在任何時(shí)候操作數(shù)堆棧都有其相應(yīng)的深度,long或者double型的值是兩個(gè)單位而其它的值是一個(gè)單位。
每個(gè)幀都包含一個(gè)相應(yīng)于當(dāng)前方法的類型的運(yùn)行時(shí)常量池的引用以支持方法代碼的動(dòng)態(tài)鏈接。類文件代碼中的方法代碼指的是被調(diào)用的方法以及通過(guò)符號(hào)引用可以訪問(wèn)的變量,動(dòng)態(tài)鏈接將這些符號(hào)方法引用翻譯為具體的方法引用、在必要的時(shí)候加載類以解析未定義的符號(hào)以及將變量訪問(wèn)翻譯為那些變量的運(yùn)行時(shí)位置在存儲(chǔ)結(jié)構(gòu)中的適當(dāng)?shù)钠?。方法和變量的晚期綁定使得方法使用到的其它類的變化可以破壞該代碼的可能性更小。
如果方法調(diào)用沒(méi)有導(dǎo)致一個(gè)異常(無(wú)論是JVM拋出的還是代碼顯式拋出的)就被認(rèn)為是方法調(diào)用正常結(jié)束。如果當(dāng)前方法調(diào)用正常結(jié)束,那么一個(gè)值可能被返回給調(diào)用它的方法。
在這種情況下,當(dāng)前幀被用于恢復(fù)調(diào)用者的狀態(tài),包括它的局部變量和操作數(shù)堆棧以及適當(dāng)增加程序計(jì)數(shù)器以跳過(guò)方法調(diào)用指令。方法調(diào)用者所在的幀的程序的執(zhí)行正常的繼續(xù),如果有方法返回,返回值被壓入幀的操作數(shù)堆棧。
如果方法里面的一個(gè)JVM指令的執(zhí)行引起JVM拋出一個(gè)異常并且那個(gè)異常在方法里面沒(méi)有被處理就會(huì)導(dǎo)致方法調(diào)用突然結(jié)束,執(zhí)行一個(gè)athrow指令也可以導(dǎo)致一個(gè)異常被顯式的拋出并且如果那個(gè)異常沒(méi)有被當(dāng)前方法捕獲也可以導(dǎo)致方法調(diào)用突然結(jié)束,一個(gè)突然結(jié)束的方法調(diào)用永遠(yuǎn)也不會(huì)向它的調(diào)用者返回一個(gè)值。
一個(gè)幀可能會(huì)被像調(diào)試信息這樣的與實(shí)現(xiàn)相關(guān)的特定信息擴(kuò)展。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn