JVM模板直譯器:位元組碼的resolve過程

發表於2015-08-10

1、背景

上文探討了:【JVM】模板直譯器–如何根據位元組碼生成彙編碼?

本篇,我們來關注下位元組碼的resolve過程。

2、問題及準備工作

上文雖然探討了位元組碼到彙編碼的過程,但是:

其中為什麼要指定0x04和0x19呢?

搬出我們的程式碼:

換句話講,我們的彙編程式碼是要將b.value賦給a.value:

b.value是個整形的field,上述程式碼的關鍵位元組碼是putfield,而模板直譯器在初始化的時候(非執行時,這也是模板的意義所在)會呼叫下面的函式來生成對應的彙編碼:

3、field、class的符號解析及連結

3.1、resolve_cache_and_index

來看看上面程式碼中的關鍵點:

上面的程式碼又有兩個關鍵點:

3.2、get_cache_and_index_and_bytecode_at_bcp

get_cache_and_index_and_bytecode_at_bcp函式,主要做的一些工作如下文所述。

cp cache指ConstantPoolCache,注意這不是一個一般意義上的快取,其目的是用於直譯器執行時,對位元組碼進行resolve的。

  1. 對給定的bytecode,在cp cache中查詢是否已經存在,如果不存在要進行resolve.至於cp cache問題,最後再說。
  2. 進行resolve的主要內容:
    — InterpreterRuntime::resolve_get_put
    — InterpreterRuntime::resolve_invoke
    — InterpreterRuntime::resolve_invokehandle
    — InterpreterRuntime::resolve_invokedynamic

3.3、resolve_get_put

因為我們的putfield位元組碼會選擇函式resolve_get_put來進行resolve,來關注這個過程:

注意tos這個點:

其中,tos是指 T op– O f– S tack,也就是運算元棧(vm實現中是expression stack)頂的東東的型別.

上面的程式碼中又標出一個關鍵點:

3.4、resolve_field_access

看程式碼:

注意到上面的程式碼還呼叫了resolve_klassresolve_field,我們一個一個看,

3.5、resolve_klass:

上面的程式碼很簡單,從常量池取出對應的klass,並同當前執行緒一起,封裝為一個KlassHandle。

3.6、resolve_field:

再接著看resolve_field:

上面的程式碼,我們梳理出三個跟本主題相關的關鍵點,已在註釋中標出,我們來看:

上面的關鍵點解析都在註釋中了,其中有的地方內容太多,不宜在本篇展開。

那麼,如何獲取當前執行的位元組碼對應的cp cache entry呢?

3.7、如何獲取cp cache entry:

關鍵程式碼如下:

上述過程總結下:

1、獲取bcp,也就是直譯器當前正在執行的位元組碼的地址,以指標形式返回.

2、bcp是通過當前執行緒的呼叫棧的最後一幀來獲取的,並且是個直譯器棧幀.為什麼是最後一幀?

每個方法在呼叫時都會用一個棧幀frame來描述呼叫的狀態資訊,最後呼叫的方法就是當前方法,所以是取最後一幀.

3、當前方法的地址是通過棧幀中儲存的interpreterState來獲取的,而這個interpreterState是個BytecodeInterpreter型的直譯器,不是模板直譯器。

4、獲取到方法的地址後,就可以獲取到方法所屬的常量池了,接著從常量池對應的cp cache中就可以獲取到對應的entry了。

5、第4點提到對應,怎麼個對應法?想象陣列的下標,這個下標是什麼呢?就是對bcp的一個整形對映。

3.8、BytecodeInterpreter的一些關鍵欄位

注意BytecodeInterpreter和TemplateInterpreter不是一碼事.

BytecodeInterpreter的一些關鍵欄位,幫助理解bcp、thread、cp、cp cache在直譯器棧幀中意義:

在進行resolve後,位元組碼就在ConstantPoolCache對應的Entry中了,下一次再執行就不需要resolve。

至於BytecodeInterpreter是個什麼直譯器,和模板直譯器有啥關係,後面再說吧。

4、結語

本文簡要探討了:

位元組碼的resolve過程。

終。

相關文章