1. 程式人生 > 實用技巧 >JVM如何執行方法呼叫

JVM如何執行方法呼叫

  從記憶體模型角度來說,每執行一個方法都會生成一個棧幀。棧幀通過運算元棧和區域性變量表來完成計算。這部分的內容不是今天的重點。

一. JVM如何識別目標方法?

  首先確定一下需求,識別目標方法。目前方法就是JVM記憶體模型中的一個記憶體地址,它是一個函式。識別就是我們的位元組碼定位到這個地址的過程。

  怎麼識別呢?JVM記憶體地址可不是固定的,需要一個固定的唯一標識來標記這塊地址,也就是計算機中常見對映表。

  這個唯一標識是什麼?我們在編寫Java類時,類的方法名可以是重複的,但是有一定的限制,這其中涉及了兩個知識:過載和重寫。

過載和重寫

  在 Java 程式裡,如果同一個類中出現多個名字相同,並且引數型別相同的方法,那麼它無法通過編譯。也就是說,在正常情況下,如果我們想要在同一個類中定義名字相同的方法,那麼它們的引數型別必須不同。這些方法之間的關係,我們稱之為過載。

小知識:這個限制可以通過位元組碼工具繞開。也就是說,在編譯完成之後,我們可以再向class檔案中新增方法名和引數型別相同,而返回型別不同的方法。當這種包括多個方法名相同、引數型別相同,而返回型別不同的方法的類,出現在Java編譯器的使用者類路徑上時,它是怎麼確定需要呼叫哪個方法的呢?當前版本的Java編譯器會直接選取第一個方法名以及引數型別匹配的方法。並且,它會根據所選取方法的返回型別來決定可不可以通過編譯,以及需不需要進行值轉換等。

  過載的目標方法在編譯時期就可以確定,在Idea中使用Ctrl+滑鼠左鍵都可以定位到目標方法,更別說更加底層的虛擬機器了。具體到每一個方法的呼叫,Java編譯器會根據方法名和所傳引數的宣告型別進行判斷:

  1.在不考慮裝箱拆箱和變長引數的情況下,定位方法

  2.若1沒找到,在考慮裝箱拆箱的情況下,定位方法

  3.若1、2沒找到,在考慮裝箱拆箱和邊長引數的情況下,定位方法

  若Java編譯器找到了多個匹配的方法,則會選擇最接近的那個,如下例:

void invoke(Object obj, Object... args) { ... }
void invoke(String s, Object obj, Object... args) { ... }

invoke(null, 1);    // 呼叫第二個invoke方法,因為String是Object的子類,傳入null時JVM認為子類是更解決的方法
invoke(null, 1, 2); // 呼叫第二個invoke方法 invoke(null, new Object[]{1}); // 只有手動繞開可變長引數的語法糖, // 才能呼叫第一個invoke方法

  注:官方文件建議避免過載可變長引數方法,程式設計師很可能在使用時誤判斷目標方法。