Java虛擬機調用一個類方法時,它會基於對象引用的類型(一般在編譯時可知)來選擇所調用的方法。相反,當虛擬機調用一個實例方法時,它會基於對象實際的類型(只能在運行時得知)來選擇所調用的方法,這就是動態綁定,是多態的一種。動態綁定爲解決實際的業務問題提供了很大的靈活性,是一種很是優美的機制。數組
Java虛擬機規範並無規定Java對象在堆裏是如何表示的。對象的內部表示也影響着整個堆以及垃圾收集器的設計,它由虛擬機的實現者決定。設計
Java對象中包含的基本數據由它所屬的類及其全部超類聲明的實例變量組成。只要有一個對象引用,虛擬機就必須可以快速地定位對象實例的數據。另外,它也必須能經過該對象引用訪問相應的類數據(存儲於方法區的類型信息),所以在對象中一般會有一個指向方法區的指針。當程序在運行時須要轉換某個對象引用爲另一種類型時,虛擬機必需要檢查這種轉換是否被容許,被轉換的對象是否的確是被引用的對象或者它的超類型。當程序在執行instanceof操做時,虛擬機也進行了一樣的檢查。因此虛擬機都須要查看被引用的對象的類數據。指針
無論虛擬機的實現使用什麼樣的對象表示法,極可能每一個對象都有一個方法表由於方法表加快了調用實例方法時的效率。可是JAVA虛擬機規範並未要求必須使用方法表,因此並非全部實現中都會使用它。對象
下面是一種Java對象的內存模型表示:內存
方法數據存放在類的方法區中,包含一個方法的具體實現的字節碼二進制。方法指針直接指向這個方法在內存中的起始位置,經過方法指針就能夠找到這個方法。虛擬機
方法表是一個指向方法區中的方法指針的數組。方法表中不包含static、private等靜態綁定的方法,僅僅包含那些須要動態綁定的實例方法。編譯
在方法表中,來自超類的方法出如今來自子類的方法以前,而且排列方法指針的順序和方法在class文件中出現的順序相同,這種排列順序的例外狀況是,被子類的方法覆蓋的方法出如今超類中該方法第一次出現的地方。class
例若有超類Base和子類Derive:test
上例中的Base和Derive的方法表以下:效率
在這個例子裏,test()方法在Base和Derive的方法表中都是同一個位置-位置1。在Base方法表中,test()指針是Base的test()方法內存地址;而在Derive方法表中,方法表的位置1放置的是Derive的test()方法內存地址。
當Java虛擬機執行base.test()時,經過base引用能夠找到base所指向的實際對象的內存位置,如今虛擬機不知道base引用的實際對象是Base仍是Derive。可是根據上面的對象內存模型,虛擬機從對象內存中的第一個指針「特殊結構指針」開始,能夠找到實際對象的類型數據和Class實例,這樣虛擬機就能夠知道base引用的實際對象是Derive對象。爲了執行test(),虛擬機須要找到test()的字節碼,方法的字節碼存放在方法區中。虛擬機從對象內存中的第一個指針「特殊結構指針」開始,搜尋方法表的位置1,位置1指向的test()方法是Derive類的test()方法,這就是JAVA虛擬機將要執行的test()的字節碼。如今,虛擬機知道了調用的實際對象是Derive對象,調用的實際test()方法是Derive類的test()方法,因此JAVA虛擬機可以正確執行-調用base引用的實際對象的方法而不是base引用自己的方法。
這是動態綁定的一種實現方式,根據不一樣的JAVA虛擬機平臺和不一樣的實際約束,動態綁定能夠有不一樣的內部實現機制。