方法調(diào)用
java程序語(yǔ)言提供了兩種基本方法:實(shí)例方法和類(靜態(tài))方法.其不同點(diǎn)是:
- 實(shí)例方法在調(diào)用前需要一個(gè)對(duì)象實(shí)例,而類方法不需要.
- 實(shí)例方法使用動(dòng)態(tài)綁定,而類方法使用靜態(tài)綁定.
JVM用invokevirtual和invokestatic兩種不同的指令來(lái)分別處理實(shí)例方法和類方法的調(diào)用.
動(dòng)態(tài)鏈接
符號(hào)引用 是一簇唯一標(biāo)識(shí)一個(gè)方法,包括類名,方法名,和方法描述(方法返回值,參數(shù)類型等)等信息的存放在常量池中的標(biāo)識(shí).當(dāng)在運(yùn)行時(shí),遇到方法調(diào)用的指令時(shí),就會(huì)解決符號(hào)引用.
為了解決符號(hào)引用,JVM定位到被符號(hào)引用的標(biāo)識(shí),并將其替換成直接引用.直接引用,比如一個(gè)指針或一個(gè)偏移量,能夠使虛擬機(jī)調(diào)用方法更快(沒(méi)有使用過(guò)).
驗(yàn)證
在將符號(hào)引用轉(zhuǎn)成直接引用時(shí),JVM會(huì)附加執(zhí)行一些驗(yàn)證檢查操作.這些檢查確保java語(yǔ)言規(guī)則被遵守并且調(diào)用指令能被安全執(zhí)行.比如,虛擬機(jī)首先確保符號(hào)引用的方法存在.如果存在,虛擬機(jī)檢查確保當(dāng)前類能合法進(jìn)入到要調(diào)用的方法.比如,如果方法是private,它必須是當(dāng)前類的成員.如果有一個(gè)檢查失敗,JVM將拋出異常.
對(duì)象引用和參數(shù)問(wèn)題
在上面準(zhǔn)備好了直接引用后,JVM將要準(zhǔn)備參數(shù),實(shí)例方法需要有對(duì)象引用.這些參數(shù)必須在調(diào)用指令之前必須通過(guò)字節(jié)碼指令push到調(diào)用方法(發(fā)起方)的操作數(shù)棧中.
Pushing and poping 棧幀
調(diào)用一個(gè)方法時(shí),JVM將為被調(diào)用方法創(chuàng)建一個(gè)新的棧幀.棧幀包括方法的本地變量表、操作數(shù)棧和該虛擬機(jī)特殊實(shí)現(xiàn)需要的其他信息.本地變量表和操作數(shù)棧的大小在編譯時(shí)就已經(jīng)計(jì)算好了.
當(dāng)方法調(diào)用時(shí)將在棧中增加一個(gè)新的棧幀就叫做Pushing一個(gè)棧幀.當(dāng)方法返回時(shí)移除一個(gè)棧幀叫做poping一個(gè)棧幀.
調(diào)用java方法
當(dāng)調(diào)用一個(gè)java方法時(shí),JVM將push一個(gè)新的棧幀到當(dāng)前的java棧中.
針對(duì)一個(gè)實(shí)例方法,VM 將pops調(diào)用方法棧幀中的對(duì)象引用和操作數(shù)棧中的參數(shù).JVM創(chuàng)建一個(gè)新的棧幀,并且將新的棧幀中本地變量的0(this)替換為對(duì)象引用,其他的1、2替換為其他參數(shù).
針對(duì)類方法,VM僅僅調(diào)用方法棧幀中的操作數(shù)棧中的參數(shù),并用他們替換新的棧幀中的本地變量0,1,2....
一旦新的棧幀中的本地變量表替換完成,VM將新的棧幀作為當(dāng)前棧幀并且設(shè)置程序計(jì)數(shù)器指向新方法的第一條指令.
The JVM specification does not require a particular implementation for the Java stack. Frames could be allocated individually from a heap, or they could be taken from contiguous memory, or both. If two frames are contiguous, however, the virtual machine can just overlap them such that the top of the operand stack of one frame forms the bottom of the local variables of the next. In this scheme, the virtual machine need not copy objectref and args from one frame to another, because the two frames overlap. The operand stack word containing objectref in the calling method's frame would be the same memory location as local variable 0 of the new frame
調(diào)用本地方法
如果調(diào)用的方法是本地方法,JVM以一種依賴實(shí)現(xiàn)的方法來(lái)調(diào)用。VM不會(huì)為本地方法向java棧push一個(gè)新的棧幀,At the point at which the thread enters the native method, it leaves the Java stack behind. When the native method returns, the Java stack once again will be used.
其他方法調(diào)用形式
盡管實(shí)例方法一般通過(guò)invokevirtual來(lái)調(diào)用,然而invokespecial和invokeinterface這兩個(gè)操作碼被用來(lái)調(diào)用某種情形的特殊方法.
invokespecial用于基于引用類型的實(shí)例化方法.用于三種情形:
- 調(diào)用實(shí)力初始化(<init>)方法
- 調(diào)用私有方法
- 調(diào)用使用
super關(guān)鍵字的方法
invokeinterface用來(lái)調(diào)用一個(gè)接口引用的實(shí)例方法.
invokespecial與invokevirtual不同點(diǎn):
invokespecial選擇的方法是基于引用類型而不是對(duì)象的類類型.或者說(shuō),是靜態(tài)綁定而不是動(dòng)態(tài)綁定。
invokespecial與私有方法
class Superclass {
private void interestingMethod() {
System.out.println("Superclass's interesting method.");
}
void exampleMethod() {
interestingMethod();
}
}
class Subclass extends Superclass {
void interestingMethod() {
System.out.println("Subclass's interesting method.");
}
public static void main(String args[]) {
Subclass me = new Subclass();
me.exampleMethod();
}
}
以上將打印:
Superclass's interesting method.
由于invokespecial,VM將選擇基于引用類型的方法,所以如此打印.
方法調(diào)用事例
interface inYourFace {
void interfaceMethod ();
}
class itsABirdItsAPlaneItsSuperClass implements inYourFace {
itsABirdItsAPlaneItsSuperClass(int i) {
super(); // invokespecial (of an <init>)
}
static void classMethod() {
}
void instanceMethod() {
}
final void finalInstanceMethod() {
}
public void interfaceMethod() {
}
}
class subClass extends itsABirdItsAPlaneItsSuperClass {
subClass() {
this(0); // invokespecial (of an <init>)
}
subClass(int i) {
super(i); // invokespecial (of an <init>)
}
private void privateMethod() {
}
void instanceMethod() {
}
final void anotherFinalInstanceMethod() {
}
void exampleInstanceMethod() {
instanceMethod(); // invokevirtual
super.instanceMethod(); // invokespecial
privateMethod(); // invokespecial
finalInstanceMethod(); // invokevirtual
anotherFinalInstanceMethod(); // invokevirtual
interfaceMethod(); // invokevirtual
classMethod(); // invokestatic
}
}
class unrelatedClass {
public static void main(String args[]) {
subClass sc = new subClass(); // invokespecial (of an <init>)
subClass.classMethod(); // invokestatic
sc.classMethod(); // invokestatic
sc.instanceMethod(); // invokevirtual
sc.finalInstanceMethod(); // invokevirtual
sc.interfaceMethod(); // invokevirtual
inYourFace iyf = sc;
iyf.interfaceMethod(); // invokeinterface
}
}
方法返回
JVM會(huì)使用針對(duì)每一種返回類型的操作來(lái)返回.返回值將從操作數(shù)棧pop并且push到調(diào)用方法的方法棧幀中.當(dāng)前的棧幀pop,被調(diào)用方法的棧幀變成當(dāng)前的.程序計(jì)數(shù)器將重置為調(diào)用這個(gè)方法的指令的下一條指令.
操作碼描述:
ireturn none pop int, push onto stack of calling method and return
lreturn none pop long, push onto stack of calling method and return
freturn none pop float, push onto stack of calling method and return
dreturn none pop double, push onto stack of calling method and return
areturn none pop object reference, push onto stack of calling method and return
-
return none return void
The ireturn instruction is used for methods that return int, char, byte, or short.
結(jié)論
- 實(shí)例方法是動(dòng)態(tài)綁定的,除了
<init>、private和super關(guān)鍵字調(diào)用的方法,這三種特殊情況,實(shí)例方法是靜態(tài)綁定的. - 類方法是靜態(tài)綁定的.
- 與接口引用相關(guān)的實(shí)例方法可能比同樣的對(duì)象關(guān)聯(lián)的方法慢.
參考鏈接
how the java virtual machine handles method invocation and return