3.動態鏈接(Dynamic Linking【指向運行時常量池的方法引用】)
每一個棧幀內部都包含有一個指向運行時常量池 中該棧幀方法的引用。包含這個引用的目的就是為了支持當前方法的代碼能夠實現動態鏈接(Dynamic Linking)。例如invokednamic指令
在Java源文件被編譯到字節碼文件中時,所有的變量和方法引用都作為符號引用(Symbolic Reference)保存在class文件的常量池。
#開頭的就是符號引用。
比如:描述一個方法調用了另外的其他方法時,就是通過常量池中指向方法的符號引用來表示的,那么動態鏈接的作用就是為了將這些符號引用轉換為調用方法的直接調用
Q:為什么需要常量池?
A:字節碼文件中需要很多數據的支持,但數據很大,不能直接保存到字節碼文件中,所以常量池的作用就是為了提供一些符號和常量,便於指令的識別。
方法的調用:解析與分派
在JVM中,將符號引用轉換為調用方法的直接引用與方法的綁定固定機制有關
靜態鏈接:當一個字節碼文件被裝進JVM內部時,如果被調用的目標方法在編譯期可知且運行期保持不變。這種情況下調用方法的符號引用轉換
為直接引用的過程稱之為靜態鏈接。
動態鏈接:如果被調用的目標方法在編譯期無法確定。這種情況下調用方法的符號引用轉換為直接引用的過程稱之為靜態鏈接。、
對應的方法綁定機制為:早期綁定和晚期綁定。綁定是一個字段、方法或者類在符號引用被替換為直接引用的過程,這僅僅發生過一次
早期綁定:
早期綁定就是指被調用的目標方法如果在編譯期可知,且運行期保持不變時,即可將這個方法與所屬的類型進行綁定,這樣一來,由於明確了被調用的目標方法究竟
是哪一個,因此也就可以使用靜態鏈接的方式將符號引用轉換為直接引用。.
晚期綁定:
被調用方法在編譯時不能被確定下來
虛函數和多態關聯密切
虛方法和非虛方法
非虛方法:
如果方法在編譯器就確定了具體的調用版本,這個版本在運行時是不可變的。這樣的方法稱為非虛方法
靜態方法、私有方法、final方法、實例構造器、父類方法都是非虛方法
其他方法就是虛方法
(子類對象的多態性的前提):1.類的繼承關系 2.方法可重寫
虛擬機中的方法調用指令
普通調用指令:
1. invokestatic: 調用靜態方法,解析階段確定唯-方法版本:
2. invokespecial:調用<init>方法、 私有及父類方法,解析階段確定唯一方法版本
3. invokevirtual: 調用所有虛方法
4. invokeinterface: 調用接口方法
動態調用指令:
5.前四條指令固化在虛擬機內部,方法的調用執行不可人為干預,而invokedynamic指令則支持由用戶確定方法版本。其中invokestatic指令和invokespecial指令調用的方法稱為非虛方法,其余的(final修飾的除外)稱為虛方法。
57P視頻有實例。
JVM字節碼指令集一直比較穩定,一直到Java7中才增加了一個invokedynamic,這是Java為了實現【動態類型語言】支持而做的一種改進。
但是Java7中並沒有提供直接生成invokedynamic指令的方法,需要借助ASM這種底層字節碼工具來產生invokedynamic指令。直到Java8Lambda表達式的出現,invokedynamic指令的生成,在Java中才有了直接的生成方式。
Java7中增加的動態語言類型支持的本質是對Java虛擬機規范的修改,而不是對Java語言規則的修改,這一塊相對來說比較復雜,增加了虛擬機中的方法調用,最直接的受益者就是運行在Java平台的動態語言編譯器。
動態類型語言和靜態類型語言
對類型的檢查是在編譯器還是運行期;再直白一點,靜態類型語言聲明變量要有明確的類型,如Java中String sss =“666”’,這就是靜態,JS:var fd = ‘666’,var=10;動態。靜態語言判斷變量的了類型信息,動態語言判斷的是變量值的類型信息。
補張圖吧:
Java語言中方法重寫的本質:
1.找到操作數棧頂的第- -個元素所執行的對象的實際類型,記作C。
2.如果在過程結束;如果不通類型C中找到與常量中的描述符合簡單名稱都相符的方法,則進行訪問權限校驗,如果通過則返回這個方法的直接引用,查找過,則返回java.lang.IllegalAccessError異常。
3.否則,按照繼承關系從下往上依次對C的各個父類進行第2步的搜索和驗證過程。
4.如果始終沒有找到合適的方法,則拋出java. lang .AbstractMethodError異常。
IllegalAccessError介紹:
程序試圖訪問或修改一個屬性或調用一個方法,這個屬性或方法,你沒有權限訪問。一般的,這個會引起編譯器異常。這個錯誤如果發生在運行時,就說明一個類發生了不兼容的改變。
虛方法表
在面向對象的編程中,會很頻繁使用到動態分配,如果在每次動態分配的過程中都要在類的方法元數據中搜索合適的目標的話就可能影響到執行效率。因此,為了提高性能,JVM采用在類的方法區建立一個虛方法表來實現。使用索引表代替查找
每個類都有一個虛方法表,存放着各個方法的實際入口。
虛方法表什么時候被創建?
虛方法表會在類加載的鏈接階段被創建並開始初始化,類的碧昂量初始值准備完成后,JVM會把該類的方法表也初始化完成。
虛方法表的指向:son類的wait()直接指向Object,hardChoice指向Son自己。。。
dog虛方法表
cat虛方法表
可卡犬虛方法表: