1. 程式人生 > 其它 >【Java學習筆記(一百零九)】之動態型別語言呼叫,invokedynamic指令,解釋執行

【Java學習筆記(一百零九)】之動態型別語言呼叫,invokedynamic指令,解釋執行

技術標籤:Java學習筆記# JVMjavajvm

本文章由公號【開發小鴿】釋出!歡迎關注!!!


老規矩–妹妹鎮樓:

一. 動態型別語言支援

(一) 概述

動態型別語言的關鍵特徵是它的型別檢查的主體過程是在執行期而不是編譯期進行的。Java語言在JDK7之前通過編譯將方法的符號引用作為方法呼叫指令的引數儲存到Class檔案中,這個符號引用包含了該方法定義在哪個具體型別中,方法的名字以及引數順序,引數型別和方法返回值等資訊,通過這個符號引用,Java虛擬機器可以翻譯出該方法的直接引用。

對於動態型別語言而言,在編譯期是無法確定方法所在的具體型別(即方法接受者不固定),變數無型別而變數值有型別,這個特點是動態型別語言的核心特徵。


(二) 動態與靜態語言的優缺點

靜態型別語言能夠在編譯器確定變數型別,編譯器可以進行全面的型別檢查,利於穩定性;

動態型別語言在執行期才確定型別,可以為開發者提供極大的靈活性,提升開發效率;

(三) 原有方法呼叫指令

JDK7以前的方法呼叫指令中,如invokevirtual, invokespecial, invokestatic, invokeinterface的第一個引數都是被呼叫的方法的符號引用,要實現Java的動態型別語言,就要找到具體的型別,可以在編譯時留個佔位符型別,執行時動態生成位元組碼實現具體型別到佔位符型別的適配。但是,這樣的記憶體消耗是很大的。

(四) 方法控制代碼

1. 概述

通過方法控制代碼可以獲取到呼叫的目標方法,lookup方法在指定類中查詢符合給定的方法名稱,方法型別並且符合呼叫許可權的方法控制代碼,返回這個方法控制代碼,即最終呼叫方法的一個引用。

2. 方法控制代碼與反射的區別

(1) 方法控制代碼和反射都是在模擬方法呼叫,但是反射是在模擬Java程式碼層次的方法呼叫,而方法控制代碼是在模擬位元組碼層次的方法呼叫;

(2) 反射中所包含的資訊比方法控制代碼中要多多了;

(3) 方法控制代碼是對位元組碼的方法指令呼叫的模擬,那麼虛擬機器在這方面做的各種優化在方法控制代碼上也可以使用,反射就沒有這些優化;

(4) 反射僅僅是為Java語言服務的,而方法控制代碼可服務與所有Java虛擬機器之上的語言;

(五) invokedynamic指令

1. 概述

該指令的作用是為了解決原有的指令方法分派規則完全固化在虛擬機器之中的問題,將如何查詢目標方法的決定權從虛擬機器轉移到具體使用者程式碼中,讓使用者有更大的自由度。

2. 動態呼叫點

每一處含有invokedynamic指令的位置都稱為動態呼叫點,這條指令的第一個引數不再是代表方法符號引用的常量,而是JDK7新加入的CONSTANT_InvokeDynamic_info常量,該常量中有三種資訊:引導方法(該方法存放在新增的BootStrapMethods屬性中),方法型別和名稱。引導方法有固定的引數,返回值是java.lang.invoke.CallSite物件,這個物件代表了真正要執行的目標方法呼叫,通過它來執行真正的方法。

3. 位元組碼解析

從動態呼叫程式碼的編譯後的位元組碼看出,原有的方法呼叫指令已經被替換為了invokedynamic了,它的第一個引數為常量池中的常量,表示方法名稱和描述符,第二個引數0是佔位符,目的是給常量池快取留出空間。


二. 位元組碼指令的執行

(一) 解釋執行

Java語言在一開始時可以理解為解釋型語言,但是隨著主流的虛擬機器都包含了即時編譯器後,Class檔案中的程式碼到底會被解釋執行還是編譯執行,就只有虛擬機器能夠決定了。經典的編譯思路是執行前先對程式原始碼進行詞法分析和語法分析,將原始碼轉換為抽象語法樹,再遍歷語法樹生成線性的位元組碼指令流,最後經過直譯器的解釋執行。

(二) 基於棧的指令集

Javac編譯器輸出的位元組碼指令流,基本上是一種基於棧的指令集架構,位元組碼指令流裡面的指令大部分都是零地址指令,依賴運算元棧工作,不需要引數,直接使用運算元棧中的資料作為指令的運算輸入,指令的運算結果也儲存在運算元棧中。如下所示:

iconst_1
iconst_1
iadd
istore_0

優點是可移植,程式碼相對更加緊湊,位元組碼中每個位元組對應一條指令,而多地址指令集中還需要存放參數,編譯器實現更加簡單,不需要考慮空間分配,直接在棧上操作。

缺點是在解釋執行前提下執行速度更慢,因為指令數量更多,出棧入棧操作產生大量指令,且棧實現在記憶體中,會產生頻繁的記憶體訪問。可以通過採取棧頂快取的方式,將最常用的操作對映到暫存器中避免記憶體直接訪問。


(三) 基於暫存器的指令集

典型的應用時x86的二地址指令集,即主流PC機中物理硬體直接支援的指令集架構,依賴暫存器工作,這種指令都包含兩個單獨的輸入引數,依賴暫存器訪問和儲存資料。如下所示:

mov eax, 1
add eax, 1