以下內(nèi)容來自 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6 自己翻譯
概述
棧幀被用來存儲(chǔ)數(shù)據(jù)和局部結(jié)果以及執(zhí)行動(dòng)態(tài)鏈接,方法和調(diào)度時(shí)異常的返回值。
每次調(diào)用方法的時(shí)候都會(huì)創(chuàng)建一個(gè)新的棧幀,當(dāng)方法調(diào)用結(jié)束的時(shí)候棧幀也隨即被銷毀,無論這個(gè)方法成功與否(會(huì)拋出未捕獲的異常)。棧幀是被java虛擬機(jī)的線程分配的,每一個(gè)棧幀都有它自己的局部變量,操作數(shù)棧和對(duì)當(dāng)前方法類的運(yùn)行時(shí)常量池的引用。
可以增加一些工具相關(guān)的信息來擴(kuò)展棧幀,例如debug信息
局部變量和操作數(shù)棧的大小在編譯時(shí)被確定,并且隨著棧幀相關(guān)的方法的代碼一起提供。因此棧幀數(shù)據(jù)結(jié)構(gòu)的大小僅僅取決于java虛擬機(jī)的實(shí)現(xiàn),并且可以在方法調(diào)用時(shí)同時(shí)的分配內(nèi)存。
在給定的線程管理下,只有一個(gè)執(zhí)行方法的棧幀是活動(dòng)的。這個(gè)棧幀被稱之為當(dāng)前幀,并且這個(gè)方法被稱之為當(dāng)前方法。擁有當(dāng)前線程執(zhí)行方法的這個(gè)類稱之為當(dāng)前類。局部變量的操作和操作數(shù)棧通常會(huì)和當(dāng)前棧幀有關(guān)。
當(dāng)一個(gè)棧幀的方法調(diào)用另一個(gè)方法或者這個(gè)方法結(jié)束時(shí),這個(gè)棧幀將不再是當(dāng)前棧幀。當(dāng)一個(gè)方法處于被調(diào)用狀態(tài),一個(gè)新的棧幀就會(huì)被創(chuàng)建,當(dāng)控制權(quán)轉(zhuǎn)移到新方法時(shí)新的棧幀會(huì)變成當(dāng)前棧幀。當(dāng)方法返回時(shí),這個(gè)當(dāng)前棧幀回傳方法的回調(diào)結(jié)果給調(diào)用方。當(dāng)前棧幀之后會(huì)被銷毀,并且之前的棧幀會(huì)變成當(dāng)前棧幀。
注意:棧幀是線程私有的不可能被其他線程引用。
局部變量表
每一個(gè)棧幀都包含一個(gè)變量數(shù)組,它被稱之為局部變量,這個(gè)棧幀的局部變量數(shù)組的長(zhǎng)度會(huì)在編譯時(shí)被確定,并且它和與幀相關(guān)的方法代碼一起在類或接口的二進(jìn)制的形式提供出來。
一個(gè)單一的局部變量可以屬于 boolean, byte, char, short, int, float, reference, 或者 返回地址類型。一對(duì)兒局部變量可以包含long或者double類型的數(shù)據(jù)
局部變量通過索引編址。第一個(gè)局部變量的地址是0。一個(gè)整數(shù)當(dāng)且僅當(dāng)介于0和局部變量數(shù)組長(zhǎng)度之間時(shí),它才能被考慮作為索引值。
一個(gè)long和double類型的局部變量會(huì)占據(jù)兩個(gè)的存儲(chǔ)單元。這樣的浮點(diǎn)型數(shù)值只能通過較小的那個(gè)索引來定位。例如一個(gè)double類型的值他的局部變量數(shù)組起始索引是n,但是它事實(shí)上占用了n 和 n+1 兩個(gè)索引值,無論如何,這個(gè)局部變量的都不能通過 n+1這個(gè)索引獲得。可以往n+1這個(gè)地址的區(qū)域存數(shù)據(jù),但是這樣做了,就無法從n地址獲得有效的內(nèi)容。
java虛擬機(jī)使用局部變量去傳遞方法調(diào)用的參數(shù)。一個(gè)類方法的調(diào)用,任何參數(shù)都會(huì)從連續(xù)的以索引值0開始的局部變量數(shù)組中被傳遞。當(dāng)實(shí)例方法調(diào)用,局部變量0索引始終用于傳遞一個(gè)調(diào)用實(shí)例方法的對(duì)象的引用(例如java編程語言中的this)。任何參數(shù)會(huì)隨著該引用接踵而來,并且參數(shù)傳遞的起始位置是1.
操作數(shù)棧
每一個(gè)棧幀都包含一個(gè)后進(jìn)先出的操作數(shù)棧,這個(gè)站的最大深度取決于編譯的結(jié)果,并且會(huì)隨著與棧先關(guān)的方法代碼一起提供。
當(dāng)包含這個(gè)操作數(shù)棧的棧幀剛被創(chuàng)建時(shí),這個(gè)操作數(shù)棧是空的。java虛擬機(jī)提供指令去加載常量或者局部變量或者字段到操作數(shù)棧中。其他的java虛擬機(jī)指令從操作數(shù)棧獲取操作數(shù),執(zhí)行相關(guān)操作指令并且將返回的結(jié)果壓入操作數(shù)棧。操作數(shù)棧還被用于準(zhǔn)備將要傳遞給方法的參數(shù)和方法返回的結(jié)果。
例如iadd指令讓兩個(gè)整型數(shù)據(jù)相加,它要求這兩個(gè)整型數(shù)據(jù)作為操作數(shù)棧的前兩位,由之前的指令壓人棧中。這兩個(gè)整型數(shù)據(jù)會(huì)被從棧中取出,然后相加并把它們的結(jié)果壓回棧中。子計(jì)算可以嵌套在其中,包含這個(gè)子計(jì)算的計(jì)算指令可以獲得這個(gè)結(jié)果。
每一個(gè)操作數(shù)棧的元素都可以擁有全部java虛擬機(jī)數(shù)據(jù)類型,包括long和double。
操作數(shù)棧中的值必須以適合其類型的方式進(jìn)行操作。例如,不能壓入兩個(gè)int值,但是用long或者兩個(gè)浮點(diǎn)的形式去壓入,并且用iadd指令去相加。少數(shù)的Java虛擬機(jī)指令(DUP指令dup和交換swap指令)將運(yùn)行時(shí)數(shù)據(jù)區(qū)域作為原始值進(jìn)行操作,而不考慮其特定類型; 這些指令不能用于修改或分解單個(gè)值。操作數(shù)棧的這些操作限制是通過class文件驗(yàn)證強(qiáng)制執(zhí)行的。
在任何時(shí)候,操作數(shù)堆棧具有相關(guān)聯(lián)的深度,其中類型值為long或 double占用兩個(gè)單位深度,任何其他類型的值占用一個(gè)單位深度。
正常返回
如果調(diào)用不會(huì)引起異常拋出,或一個(gè)顯式的異常拋出語句被執(zhí)行,那么這個(gè)方法調(diào)用就是正常完成,并且返回值會(huì)交還給調(diào)用者。如果發(fā)生正常方法調(diào)用完成 return指令必須已核實(shí)的方式返回合適類型的值。
當(dāng)前棧幀用于恢復(fù)調(diào)用者的狀態(tài),包括其局部變量和操作數(shù)對(duì)戰(zhàn)。調(diào)用者的程序計(jì)數(shù)器會(huì)增加用來跳過方法調(diào)用指令。然后方法調(diào)用者將會(huì)繼續(xù)正常執(zhí)行,他的棧幀會(huì)到操作數(shù)棧中壓入返回的結(jié)果。
異常返回
指一個(gè)方法調(diào)用翻車,調(diào)用指令的時(shí)候虛擬機(jī)拋出異常,并且異常沒有在方法中處理。指令athrow的執(zhí)行會(huì)導(dǎo)致異常明確拋出并且如果當(dāng)前方法沒有捕獲異常,將會(huì)產(chǎn)生意外的方法調(diào)用完成。這個(gè)意外的方法調(diào)用完成不會(huì)給調(diào)用者返回任何值