JVM系列-方法呼叫的原理
最近重新看了一些JVM方面的筆記和資料,收穫頗豐,尤其解決了長久以來心中關於JVM方法管理的一些疑問。下面介紹一下JVM中有關方法呼叫的知識。
目的
方法呼叫,目的是選擇方法正確的執行版本,也就是找到方法的入口地址。
方法呼叫指令
方法呼叫的位元組碼指令一共有五種,分別是:
- invokestatic:
- 類方法:static
- invokespecial:
方法:例項構造器 - 私有方法:private
- 父類中的方法
- invokevirtual
- 虛方法
- final修飾的方法
- invokeinterface
- 介面方法
- invokedynamic
- 用於動態語言支援
方法分類
非虛方法:
- 特點:
- 在類載入時期,有些方法的呼叫版本已經能夠確定,在類載入-解析階段,把這些方法的符號引用替換為直接引用(即入口地址)。
- 包括:
- 類方法
方法 - 私有方法
- 父類中的方法
- final方法
即由invokestatic、invokespecial指令呼叫的方法,以及final修飾的方法,屬於非虛方法
虛方法:
- 特點:
- 在類載入時期,無法確定方法最終的呼叫版本
- 包括:
- 非非虛方法
即由invokevirtual呼叫的方法,除了final修飾的之外,都是虛方法
呼叫方式
解析
- 定義:對於非虛方法,在類載入的時候已經能確定這些方法的呼叫版本,在類載入的解析階段把符號引用替換為直接引用,就是方法的入口地址
- 範圍:非虛方法
分派
靜態分派
- 定義:在編譯期,根據方法涉及的引用型別(包括引數列表的引用型別和方法的呼叫者的引用型別)來確定方法呼叫的(初步)版本,並把相應的符號引用放在位元組碼指令中,這個步驟叫做靜態分派
- 範圍:虛方法、非虛方法
- 應用:方法的過載
動態分派
- 定義:在執行時,根據物件的實際型別來確定方法的呼叫版本,這個步驟叫做動態分派
- 範圍:虛方法
- 應用:方法的重寫
- 原理:
- 方法呼叫的部分位元組碼指令:
- aload:在呼叫方法時,總是有該指令將實際型別的物件引用入棧
- 載入方法引數
- invoketual
- 先在棧頂取到實際型別的物件引用
- 根據方法的符號引用在本類中尋找,找不到的話依次向上在父類中找該方法
- 優化實現:在虛擬機器中,虛方法表是動態分派的一種優化實現
- 虛方法表:
- 定義:類資訊的一種,在類載入-準備階段完成初始化,存放在方法區
- 特點:
- 虛方法表中儲存方法的入口地址
- 子類中繼承但未重寫的方法,在子類的虛方法表中存放的入口地址,就是父類的虛方法表中的入口地址,指向父類的實現
- 在父子類中,相同符號引用的方法(重寫)的方法,其在各自虛方法表中的索引相同
- 解釋動態分派:
- 根據位元組碼指令的符號引用,找到該方法在父類虛方法表中的索引
- 切換到實際型別的虛方法表,通過該索引,找到方法的入口地址
- 虛方法表:
- 方法呼叫的部分位元組碼指令:
總結
主要內容在於方法呼叫的方式,即如何找到方法的入口地址的過程。
在這三種方式中,靜態分派發生在編譯期,解析發生在類載入時,動態分派發生在執行時。針對三者作用物件的不同可知,靜態分派是可以和解析或動態分派同時發生的,如類方法依然可以過載,重寫的方法也可以過載。但解析和動態分派是不可共存的。
以上是我的分享,如有疑問或錯誤,請指出,一起學習。
參考
《深入理解Java虛擬機器_JVM高階特性與最佳實踐-第3版》