方法調(diào)用

方法調(diào)用就3塊,解析稍坯,分派酬荞,動(dòng)態(tài)語言支持(這塊讀者可以自行百度,這篇暫且不表)瞧哟。

解析

所有方法調(diào)用中的目標(biāo)方法在 Class 文件里面都是一個(gè)常量池中的符號(hào)引用混巧,在類加載的解析階段,會(huì)將其中的一部分符號(hào)引用轉(zhuǎn)化為直接引用勤揩,這種解析能成立的前提是:方法在程序真正運(yùn)行之前就有一個(gè)可確定的調(diào)用版本咧党,并且這個(gè)方法的調(diào)用版本在運(yùn)行期是不可改變的。換句話說陨亡,調(diào)用目標(biāo)在程序代碼寫好傍衡、編譯器進(jìn)行編譯時(shí)就必須確定下來。這類方法的調(diào)用稱為解析(Resolution)负蠕。

在 Java 語言中符合 “編譯期可知蛙埂,運(yùn)行期不可變” 這個(gè)要求的方法,主要包括靜態(tài)方法和私有方法兩大類遮糖,前者與類型直接關(guān)聯(lián)绣的,后者在外部不可被訪問,這兩種方法各自的特點(diǎn)決定了它們都不可能通過繼承或別的方式重寫其他版本,因此它們都適合在類加載階段進(jìn)行解析屡江。

與之相對(duì)應(yīng)的是芭概,在 Java 虛擬機(jī)里面提供了 5 條方法調(diào)用字節(jié)碼指令,分別如下惩嘉。

  • invokestatic:調(diào)用靜態(tài)方法罢洲。
  • invokespecial:調(diào)用實(shí)例構(gòu)造器 <init> 方法、私有方法和父類方法文黎。
  • invokevirtual::調(diào)用所有的虛方法惹苗。
  • invokeinterface:調(diào)用接口方法,會(huì)在運(yùn)行時(shí)再確定一個(gè)實(shí)現(xiàn)此接口的對(duì)象臊诊。
  • invokedynamic:先在運(yùn)行時(shí)動(dòng)態(tài)解析出調(diào)用點(diǎn)限定符所引用的方法鸽粉,然后再執(zhí)行該方法斜脂,再次之前的 4 條調(diào)用指令抓艳,分派邏輯是固化在 Java 虛擬機(jī)內(nèi)部的,而 invokedynamic 指令的分配邏輯是由用戶所設(shè)定的引導(dǎo)方法決定的帚戳。

只要能被 invokestatic 和 invokespecial 指令調(diào)用的方法玷或,都可以在解析階段確定唯一的調(diào)用版本,符合這個(gè)條件的有靜態(tài)方法片任、私有方法偏友、實(shí)例構(gòu)造器、父類方法 4 類对供,它們?cè)陬惣虞d的時(shí)候就會(huì)把符號(hào)引用解析為該方法的直接引用位他。這些方法可以稱為非虛方法。與之相反产场,其他方法稱為虛方法(除去 final 方法鹅髓,后文會(huì)提到)。代碼清單 8-5 演示了一個(gè)最常見的解析調(diào)用的例子京景,此樣例中窿冯,靜態(tài)方法 sayHello() 只可能屬于類型 StaticResolution,沒有任何手段可以覆蓋或隱藏這個(gè)方法确徙。

Java 中的非虛方法除了使用 invokestatic醒串、invokespecial 調(diào)用的方法之外還有一種,就是被 final 修飾的方法鄙皇。雖然 final 方法是使用 invokevirtual 指令來調(diào)用的芜赌,但是由于它無法被覆蓋,沒有其他版本伴逸,所以也無須對(duì)方法接收者進(jìn)行多態(tài)選擇缠沈,又或者說多態(tài)選擇的結(jié)果肯定是唯一的。在 Java 語言規(guī)范中明確說明了 final 方法是一種非虛方法。

解析調(diào)用一定是個(gè)靜態(tài)的過程博烂,在編譯期間就完全確定香椎,在類裝載的解析階段就會(huì)把涉及的符號(hào)引用全部轉(zhuǎn)變?yōu)榭纱_定的直接引用,不會(huì)延遲到運(yùn)行期再去完成禽篱。而分派(Dispatch)調(diào)用則可能是靜態(tài)的也可能是動(dòng)態(tài)的畜伐,根據(jù)分派依據(jù)的宗量數(shù)可分為單分派和多分派。這兩類分派方式的兩兩組合就構(gòu)成了靜態(tài)單分派躺率、靜態(tài)多分派玛界、動(dòng)態(tài)單分派、動(dòng)態(tài)多分派 4 種分派組合情況悼吱。

分派

眾所周知慎框,Java 是一門面向?qū)ο蟮某绦蛘Z言,因?yàn)?Java 具備面向?qū)ο蟮?3 個(gè)基本特征:繼承后添、封裝和多態(tài)笨枯。本節(jié)講解的分派調(diào)用過程將會(huì)揭示多態(tài)性特征的一些最基本的體現(xiàn),如 “重載” 和 “重寫” 在 Java 虛擬機(jī)之中是如何實(shí)現(xiàn)的遇西,這里的實(shí)現(xiàn)當(dāng)然不是語法上該如何寫馅精,我們關(guān)心的依然是虛擬機(jī)如何確定正確的目標(biāo)方法。

1.靜態(tài)分派

靜態(tài)分派(嚴(yán)格來說粱檀,Dispatch 這個(gè)詞一般不用再靜態(tài)環(huán)境中洲敢,英文技術(shù)文檔的稱呼是 “Method Overload Resolution”,但國內(nèi)的各種資料都普遍將這種行為翻譯成 “靜態(tài)分派”茄蚯,特此說明)前压彭,筆者準(zhǔn)備了一段經(jīng)常出現(xiàn)在面試題中的程序代碼,讀者不妨先看一遍渗常,想一下程序的輸出結(jié)果是什么壮不。后面我們的話題將圍繞這個(gè)類的方法來重載(Overload)代碼,以分析虛擬機(jī)和編譯器確定方法版本的過程凳谦。方法靜態(tài)分派如代碼清單 8-6 所示忆畅。

代碼清單 8-6 方法靜態(tài)分派演示


public class StaticDispatch {

    static abstract class Human {
        
    }
    
    static class Man extends Human {
        
    }
    
    static class Woman extends Human {
        
    }
    
    public void sayHello(Human guy) {
         System.out.println("hello, guy!");
    }
    
    public void sayHello(Man guy) {
        System.out.println("hello,gentleman");
    }
    
    public void sayHello(Woman guy) {
        System.out.println("hello,lady!");
    }
    
    public static void main(String[] args) {
        Human man = new Man();
        Human woman = new Woman();
        
        StaticDispatch sr = new StaticDispatch();
        sr.sayHello(man);
        sr.sayHello(woman);
    }
}
    運(yùn)行結(jié)果:
image
  1. Human man = new Man();

我們把上面代碼中的 “Human” 稱為變量的靜態(tài)類型(Static Type),或者叫做外觀類型(Apparent Type)尸执,后面的 “Man” 則稱為變量的實(shí)際類型(Actual Type)家凯,靜態(tài)類型和實(shí)際類型在程序中都可以發(fā)生一些變化,區(qū)別是靜態(tài)類型的變化僅僅在使用時(shí)發(fā)生如失,變量本身的靜態(tài)類型不會(huì)被改變绊诲,并且最終的靜態(tài)類型是在編譯期可知的;而實(shí)際類型變化的結(jié)果在運(yùn)行期才可確定褪贵,編譯器在編譯程序的時(shí)候并不知道一個(gè)對(duì)象的實(shí)際類型是什么掂之。

回到代碼清單 8-6 的樣例代碼中抗俄。main() 里面的兩次 sayHello() 方法調(diào)用,在方法接收者已經(jīng)確定是對(duì)象 “sr” 的前提下世舰,使用哪個(gè)重載版本动雹,就完全取決于傳入擦你數(shù)的數(shù)量和數(shù)據(jù)類型。代碼中刻意地定義了兩個(gè)靜態(tài)類型相同但實(shí)際類型不同的變量跟压,但虛擬機(jī)(準(zhǔn)確地說是編譯器)在重載時(shí)是通過參數(shù)的靜態(tài)類型而不是實(shí)際類型作為判定依據(jù)的胰蝠。并且靜態(tài)類型是編譯期可知的,因此震蒋,在編譯階段茸塞,javac 編譯器會(huì)根據(jù)參數(shù)的靜態(tài)類型決定使用哪個(gè)重載版本,所以選擇了 sayHello(Human) 作為調(diào)用目標(biāo)查剖,并把這個(gè)方法的符號(hào)引用寫到 main() 方法里的兩條 invokevirtual 指令的參數(shù)中钾虐。

所有依賴靜態(tài)類型來定位方法執(zhí)行版本的分派動(dòng)作稱為靜態(tài)分派。靜態(tài)分派的典型應(yīng)用是方法重載笋庄。靜態(tài)分派發(fā)生在編譯階段效扫,因此確定靜態(tài)分派的動(dòng)作實(shí)際上不是由虛擬機(jī)來執(zhí)行的。另外无切,編譯器雖然能確定出方法的重載版本荡短,但在很多情況下這個(gè)重載版本并不是 “唯一的”丐枉,往往只能確定一個(gè) “更加適合的” 版本哆键。這種模糊的結(jié)論在由 0 和 1 構(gòu)成的計(jì)算機(jī)世界中算是比較 “稀罕” 的事情,產(chǎn)生這種模糊結(jié)論的主要原因是字面量不需要定義瘦锹,所以字面量沒有顯式的靜態(tài)類型籍嘹,它的靜態(tài)類型只能通過語言上的規(guī)則去理解和推斷。代碼清單 8-7 演示了何為 “更加適合的” 版本弯院。

代碼清單 8-7 重載方法匹配優(yōu)先級(jí)


public class Overload {

    public static void sayHello(Object arg) {
        System.out.println("hello Object");
    }
    
    public static void sayHello(int arg) {
        System.out.println("hello int");
    }
    
    public static void sayHello(long arg) {
        System.out.println("hello long");
    }
    
    public static void sayHello(Character arg) {
        System.out.println("hello Character");
    }
    
    public static void sayHello(char arg) {
        System.out.println("hello char");
    }
    
    public static void sayHello(char... arg) {
        System.out.println("hello char ...");
    }
    
    public static void sayHello(Serializable arg) {
        System.out.println("hello Serializable");
    }
    
    public static void main(String[] args) {
        sayHello('a');
    }
}

上面的代碼運(yùn)行后會(huì)輸出:

  1. hello char

    這很好理解辱士,'a' 是一個(gè) char 類型的數(shù)據(jù),自然會(huì)尋找參數(shù)類型為 char 的重載方法听绳,如果注釋掉 sayHello(char arg) 方法颂碘,那輸出會(huì)變?yōu)椋?
  2. hello int

    這時(shí)發(fā)生了一次自動(dòng)類型轉(zhuǎn)換,'a' 除了可以代表一個(gè)字符串椅挣,還可以代表數(shù)字 97(字符 'a' 的 Unicode 數(shù)值為十進(jìn)制數(shù)字 97)头岔,因此參數(shù)類型為 int 的重載也是合適的。我們繼續(xù)注釋掉 sayHello(int arg) 方法鼠证,那輸出會(huì)變?yōu)椋?
  3. hello long

    這時(shí)發(fā)生了兩次自動(dòng)類型轉(zhuǎn)換峡竣,'a' 轉(zhuǎn)型為整型 97 之后,進(jìn)一步轉(zhuǎn)型為長整數(shù) 97L量九,匹配了參數(shù)類型為 long 的重載适掰。筆者在代碼中沒有寫其他的類型如 float颂碧、double 等的重載,不過實(shí)際上自動(dòng)轉(zhuǎn)型還能繼續(xù)發(fā)生多次类浪,按照 char -> int -> long -> float -> double 的順序轉(zhuǎn)型進(jìn)行匹配载城。但不會(huì)匹配到 byte 和 short 類型的重載,因?yàn)?char 到 byte 或 short 的轉(zhuǎn)型是不安全的费就。我們繼續(xù)注釋掉 sayHello(long arg) 方法个曙,那輸出會(huì)變?yōu)椋?
  4. hello Character

    這時(shí)發(fā)生了一次自動(dòng)裝箱,'a' 被包裝為它的封裝類型 java.lang.Character受楼,所以匹配到了參數(shù)類型為 Character 的重載垦搬,繼續(xù)注釋掉 sayHello(Character arg) 方法,那輸出會(huì)變?yōu)椋?
  5. hello Serializable

    這個(gè)輸出可能會(huì)讓人感覺摸不著頭腦艳汽,一個(gè)字符或數(shù)字與序列化有什么關(guān)系猴贰?出現(xiàn) hello Serializable,是因?yàn)?java.lang.Serializable 是 java.lang.Character 類實(shí)現(xiàn)的一個(gè)接口河狐,當(dāng)自動(dòng)裝箱之后發(fā)現(xiàn)還是找不到裝箱類米绕,但是找到了裝箱類實(shí)現(xiàn)了的接口類型,所以緊接著又發(fā)生一次自動(dòng)轉(zhuǎn)型馋艺。char 可以轉(zhuǎn)型成 int栅干,但是 Character 是絕對(duì)不會(huì)轉(zhuǎn)型為 Integer 的,它只能安全地轉(zhuǎn)型為它實(shí)現(xiàn)的接口或父類捐祠。Character 還實(shí)現(xiàn)了另外一個(gè)接口 java.lang.Comparable<Character>碱鳞,如果同時(shí)出現(xiàn)兩個(gè)參數(shù)分別為 Serializable 和 Comparable<Character> 的重載方法,那它們?cè)诖藭r(shí)的優(yōu)先級(jí)是一樣的踱蛀。編譯器無法確定要自動(dòng)轉(zhuǎn)型為哪種類型窿给,會(huì)提示類型模糊,拒絕編譯率拒。程序必須在調(diào)用時(shí)顯式地指定字面量的靜態(tài)類型崩泡,如:sayHello((Comparable<Character>'a'),才能編譯通過猬膨。下面繼續(xù)注釋掉 sayHello(Serializable arg) 方法角撞,輸出會(huì)變?yōu)椋?
  6. hello Object

    這時(shí)是 char 裝箱后轉(zhuǎn)型為父類了,如果有多個(gè)父類勃痴,那將在繼承關(guān)系中從下往上開始搜索谒所,越接近上層的優(yōu)先級(jí)越低。即使方法調(diào)用傳入的才參數(shù)值為 null 時(shí)召耘,這個(gè)規(guī)則仍然適用百炬。我們把 sayHello(Object arg) 也注釋掉,輸出將會(huì)變?yōu)椋?
  7. hello char ...

7 個(gè)重載方法已經(jīng)被注釋得只剩一個(gè)了污它,可見變長參數(shù)的重載優(yōu)先級(jí)是最低的剖踊,這時(shí)候字符 'a' 被當(dāng)做了一個(gè)數(shù)組元素庶弃。筆者適用的是 char 類型的變長參數(shù),讀者在驗(yàn)證時(shí)還可以選擇 int 類型德澈、Character 類型歇攻、Object 類型等變長參數(shù)重載來把上面的過程重新演示一遍。

2.動(dòng)態(tài)分派

了解了靜態(tài)分派梆造,我們接下來看一下動(dòng)態(tài)分派的過程缴守,它和動(dòng)態(tài)性的另外一個(gè)重要體現(xiàn)(注:有一種觀點(diǎn)認(rèn)為:因?yàn)橹剌d是靜態(tài)的,重寫是動(dòng)態(tài)的镇辉,所以只有重寫算是多態(tài)性的體現(xiàn)屡穗,重載不算多態(tài)。筆者認(rèn)為這種整理沒有意義忽肛,概念僅僅是說明問題的一種工具而已)——重寫(Override)有著密切的關(guān)聯(lián)村砂。我們還是用前面的 Man 和 Woman 一起 sayHello 的例子來講解動(dòng)態(tài)分派,請(qǐng)看代碼清單 8-8 中所示的代碼屹逛。

代碼清單 8-8 方法動(dòng)態(tài)分派演示


public class DynamicDispatch {

    static abstract class Human {
        protected abstract void sayHello();
    }
    
    static class Man extends Human {
        @Override
        protected void sayHello() {
            System.out.println("man say hello");
        }
    }
    
    static class Woman extends Human {
        @Override
        protected void sayHello() {
            System.out.println("woman say hello");
        }
    }
    
    public static void main(String[] args) {
        Human man = new Man();
        Human woman = new Woman();
        man.sayHello();
        woman.sayHello();
        man = new Woman();
        man.sayHello();
    }
}

運(yùn)行結(jié)果:

  1. man say hello
  2. woman say hello
  3. woman say hello

這個(gè)運(yùn)行結(jié)果相信不會(huì)出乎任何人的意料础废,對(duì)于習(xí)慣了面向?qū)ο笏季S的 Java 程序員會(huì)覺得這是完全理所當(dāng)然的。現(xiàn)在的問題還是和前面的一樣罕模,虛擬機(jī)是如何知道要調(diào)用哪個(gè)方法的评腺?

    雖然這里不可能再根據(jù)靜態(tài)類型來決定,因?yàn)殪o態(tài)類型同樣都是 Human 的兩個(gè)變量 man 和 woman 在調(diào)用 sayHello() 方法時(shí)執(zhí)行了不同的行為淑掌,并且變量 man 在兩次調(diào)用中執(zhí)行了不同的方法蒿讥。導(dǎo)致這個(gè)現(xiàn)象的原因很明顯,是這兩個(gè)變量的實(shí)際類型不同锋拖,Java 虛擬機(jī)是如何根據(jù)實(shí)際類型來分派方法執(zhí)行版本的呢诈悍?我們使用 javap 命令輸出這段代碼的字節(jié)碼,嘗試從中尋找答案兽埃,輸出結(jié)果如代碼清單 8-9 所示。

代碼清單 8-9 main() 方法的字節(jié)碼

1.  public static void main(java.lang.String[]);  
2.  flags: ACC_PUBLIC, ACC_STATIC  
3.  Code:  
4.  stack=2, locals=3, args_size=1  
5.  0: new           #16                 // class org/fenixsoft/polymorphic/DynamicDispatch$Man  
6.  3: dup  
7.  4: invokespecial #18                 // Method org/fenixsoft/polymorphic/DynamicDispatch$Man."<init>":()V  
8.  7: astore_1  
9.  8: new           #19                 // class org/fenixsoft/polymorphic/DynamicDispatch$Woman  
10.  11: dup  
11.  12: invokespecial #21                 // Method org/fenixsoft/polymorphic/DynamicDispatch$Woman."<init>":()V  
12.  15: astore_2  
13.  16: aload_1  
14.  17: invokevirtual #22                 // Method org/fenixsoft/polymorphic/DynamicDispatch$Human.sayHello:()V  
15.  20: aload_2  
16.  21: invokevirtual #22                 // Method org/fenixsoft/polymorphic/DynamicDispatch$Human.sayHello:()V  
17.  24: new           #19                 // class org/fenixsoft/polymorphic/DynamicDispatch$Woman  
18.  27: dup  
19.  28: invokespecial #21                 // Method org/fenixsoft/polymorphic/DynamicDispatch$Woman."<init>":()V  
20.  31: astore_1  
21.  32: aload_1  
22.  33: invokevirtual #22                 // Method org/fenixsoft/polymorphic/DynamicDispatch$Human.sayHello:()V  
23.  36: return  

0 ~ 15 行的字節(jié)碼是準(zhǔn)備動(dòng)作适袜,作用是建立 man 和 woman 的內(nèi)存空間柄错、調(diào)用 Man 和 Woman 類型的實(shí)例構(gòu)造器,將這兩個(gè)實(shí)例的引用存放在第 1苦酱、2 個(gè)局部變量表 Slot 之中售貌,這個(gè)動(dòng)作也就對(duì)應(yīng)了代碼中的這兩句:

  1. Human man = new Man();
  2. Human woman = new Woman();

接下來的 16 ~ 21 句是關(guān)鍵部分、16疫萤、20 兩句分別把剛剛創(chuàng)建好的兩個(gè)對(duì)象的引用壓入到棧頂颂跨,這兩個(gè)對(duì)象是將要執(zhí)行的sayHello() 方法的所有者,稱為接收者 (Receiver)扯饶;17 和 21 句是方法調(diào)用指令恒削,這兩條調(diào)用指令但從字節(jié)碼角度來看池颈,無論是指令(都是 invokevirtual)還是參數(shù)(都是常量池中第 22 項(xiàng)的常量,注釋顯示了這個(gè)常量是 Human.sayHello() 的符號(hào)引用)完全一樣的钓丰,但是這兩句指令最終執(zhí)行的目標(biāo)方法并不相同躯砰。原因就需要從 invokevirtual 指令的多態(tài)查找過程開始說起,invokevirtual 指令的運(yùn)行時(shí)解析過程大致分為以幾個(gè)步驟:

  1. 找到操作數(shù)棧頂?shù)牡谝粋€(gè)元素所執(zhí)行的對(duì)象的實(shí)際類型携丁,記作 C琢歇。
  2. 如果在類型 C 中找到與常量中的描述符合簡單名稱都相符的方法,則進(jìn)行訪問權(quán)限校驗(yàn)梦鉴,如果通過則返回這個(gè)方法的直接引用李茫,查找過程結(jié)束;如果不通過肥橙,則返回 java.lang.IllegalAccessError 異常涌矢。
  3. 否則,按照繼承關(guān)系從下往上一次對(duì) C 的各個(gè)父類進(jìn)行第 2 步的搜索和驗(yàn)證過程快骗。
  4. 如果始終沒有找到合適的方法娜庇,則拋出 java.lang.AbstractMethodError 異常。

由于 invokevirtual 指令執(zhí)行的第一步就是在運(yùn)行期確定接收者的實(shí)際類型方篮,所以兩次調(diào)用中的invokevirtual指令把常量池中的類方法符號(hào)引用解析到了不同的直接引用上名秀,這個(gè)過程就是Java 語言中方法重寫的本質(zhì)。我們把這種在運(yùn)行期根據(jù)實(shí)際類型確定方法執(zhí)行版本的分派過程稱為動(dòng)態(tài)分派藕溅。

虛擬機(jī)動(dòng)態(tài)分派的實(shí)現(xiàn)

前面介紹的分派過程匕得,作為對(duì)虛擬機(jī)概念模型的解析基本上已經(jīng)足夠了,它已經(jīng)解決了虛擬機(jī)在分派中 “會(huì)做什么” 這個(gè)問題巾表。但是虛擬機(jī) “具體是如何做到的”汁掠,可能各種虛擬機(jī)的實(shí)現(xiàn)都會(huì)有些差別。

由于動(dòng)態(tài)分派是非常頻繁的動(dòng)作集币,而且動(dòng)態(tài)分派的方法版本選擇過程需要運(yùn)行時(shí)在類的方法元數(shù)據(jù)中搜索合適的目標(biāo)方法考阱,因此在虛擬機(jī)的實(shí)際實(shí)現(xiàn)中基于性能的考慮,大部分實(shí)現(xiàn)都不會(huì)真正地進(jìn)行如此頻繁的搜索鞠苟。面對(duì)這種情況乞榨,最常用的 “穩(wěn)定優(yōu)化” 手段就是為類在方法區(qū)中建立一個(gè)虛方法表(Virtual Method Table,也稱為 vtable当娱,與此對(duì)應(yīng)的吃既,在 invokeinterface 執(zhí)行時(shí)也會(huì)用到接口方法表——Interface Method Table,簡稱 itable)跨细,使用虛方法表索引來代替元數(shù)據(jù)查找以提高性能鹦倚。我們先看看但清單 8-10 所對(duì)應(yīng)的虛方法表結(jié)構(gòu)示例,如圖 8-3 所示冀惭。

image

虛方法表中存放著各個(gè)方法的實(shí)際入口地址震叙。如果某個(gè)方法在子類中沒有被重寫掀鹅,那子類的虛方法表里面的地址入口和父類相同方法的地址入口是一致的;都指向父類的實(shí)現(xiàn)入口捐友。如果子類中重寫了這個(gè)方法淫半,子類方法表中的地址將會(huì)替換為指向子類實(shí)現(xiàn)版本的入口地址。圖 8-3 中匣砖,Son 重寫了來自 Father 的全部方法科吭,因此 Son 的方法表沒有指向 Father 類型數(shù)據(jù)的箭頭。但是 Son 和 Father 都沒有重寫來自 Object 的方法猴鲫,所以它們的方法表中所有從 Object 繼承來的方法都指向了 Object 的數(shù)據(jù)類型对人。

為了程序?qū)崿F(xiàn)上的方便,具有相同簽名的方法拂共,在父類牺弄、子類的虛方法表中都應(yīng)該具有一樣的索引序號(hào),這樣當(dāng)類型變換時(shí)宜狐,僅需要變更查找的方法表势告,就可以從不同的虛方法表中按索引轉(zhuǎn)換出所需的入口地址。

方法表一般在類加載的連接階段進(jìn)行初始化抚恒,準(zhǔn)備了類的變量初始值后咱台,虛擬機(jī)會(huì)把該類的方法表也初始化完畢。

上文中筆者說方法表是分派調(diào)用的 “穩(wěn)定優(yōu)化” 手段俭驮,虛擬機(jī)除了使用方法表之外回溺,在條件允許的情況下,還會(huì)使用內(nèi)聯(lián)緩存(Inline Cache)和基于 “類型繼承關(guān)系分析”(Class Hierarchy Analysis混萝,CHA)技術(shù)的守護(hù)內(nèi)聯(lián)(Guarded Inlining)兩種非穩(wěn)定的 “激進(jìn)優(yōu)化” 手段來獲得更高的性能遗遵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市逸嘀,隨后出現(xiàn)的幾起案子车要,更是在濱河造成了極大的恐慌,老刑警劉巖厘熟,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屯蹦,死亡現(xiàn)場離奇詭異,居然都是意外死亡绳姨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門阔挠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來飘庄,“玉大人,你說我怎么就攤上這事购撼」蛳鳎” “怎么了谴仙?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碾盐。 經(jīng)常有香客問我晃跺,道長,這世上最難降的妖魔是什么毫玖? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任掀虎,我火速辦了婚禮,結(jié)果婚禮上付枫,老公的妹妹穿的比我還像新娘烹玉。我一直安慰自己,他們只是感情好阐滩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布二打。 她就那樣靜靜地躺著,像睡著了一般掂榔。 火紅的嫁衣襯著肌膚如雪继效。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天装获,我揣著相機(jī)與錄音瑞信,去河邊找鬼。 笑死饱溢,一個(gè)胖子當(dāng)著我的面吹牛喧伞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绩郎,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼潘鲫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肋杖?” 一聲冷哼從身側(cè)響起溉仑,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎状植,沒想到半個(gè)月后浊竟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡津畸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年振定,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肉拓。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡后频,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卑惜,我是刑警寧澤膏执,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站露久,受9級(jí)特大地震影響更米,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毫痕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一征峦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镇草,春花似錦眶痰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至因宇,卻和暖如春七婴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背察滑。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國打工打厘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人贺辰。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓户盯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饲化。 傳聞我的和親對(duì)象是個(gè)殘疾皇子莽鸭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容