執行引擎在執行位元組碼的時候,通常會有解釋執行(通過直譯器執行)和編譯執行(通過即時編譯器編譯成原生代碼)
public statci void main(String[] args){
{
byte[] placeholder=new byte[64*1024×1024];
}
System.gc();
}
該程式碼呼叫gc後並沒有回收placeholder的記憶體,原因是離開它的作用域後並沒有發生對區域性變數表的讀寫操作,變數槽還沒有被其他變數複用,所以作爲GC Roots一部分的區域性變數表仍保持着對他的關聯。將其設定爲null或在域後定義變數(如int i=0;)就可將其回收。
5條方法呼叫位元組碼指令:
1.invokestatic:用於呼叫靜態方法。
2.invokespecial:擁有呼叫範例構造器<init>方法、私有方法和父類別方法。
3.invokevirtual:用於呼叫所有的虛擬函式。
4.invokeinterface:用於呼叫介面方法
5.invokedynamic:先在執行時動態解析出調用點限定符所參照的方法,然後再執行。
只要能被iinvokestatic和invokespecial指令呼叫的方法,都可以在解析階段確定方法唯一的呼叫版本,符合這個的方法有靜態方法、私方法、範例構造器、父類別方法、,在加上final修飾的方法(雖然他使用invokevirtual指令),這些方法成爲虛方法,其他方法稱爲非虛方法。
靜態分派
所有依賴靜態型別來決定方法執行版本的分派動作,都成爲靜態分派。如方法過載中,它是根據參數的靜態型別來確定呼叫的版本,而靜態型別是在編譯期就是可知的。
動態分派
invokevirtual指令執行的第一步就是在執行期間確定接受者的實際型別,再根據實際型別呼叫方法版本。
invokevirtual指令執行時解析大致分爲以下幾步:
方法的接受者與方法的參數統稱爲方法的總量。
動態分派執行是非常頻繁的動作,會對效能帶來一定的影響,常見的優化手段是爲型別在方法區建立一個虛方法表,與此對應的,在invokeinterface執行時也會用到介面方法表,使用虛方法表索引來代替元數據查詢以提高效能。
如果某個方法在子類中沒有被重寫,那子類的虛方法表中的地址入口和父類別相同方法的地址入口是一致的,都是指向父類別的實現入口。
爲了程式實現方便,具有相同簽名的方法在父類別,子類的虛方法表中應當具有相同的索引號,這樣當型別變化時僅需要變更查詢的虛方法表就可以找到不同方法的入口。
虛方法表一般在準備了類的變數初始化值後,虛擬機器會把其虛方法表也一同初始化。
動態型別語言的關鍵特徵是它的型別檢查的主體過程是在執行期而不是編譯期,如Erlang等。