第36篇-return字節碼指令


方法返回的字節碼相關指令如下表所示。 

0xac

ireturn

從當前方法返回int

0xad

lreturn

從當前方法返回long

0xae

freturn

從當前方法返回float

0xaf

dreturn

從當前方法返回double

0xb0

areturn

從當前方法返回對象引用

0xb1

return

從當前方法返回void

模板定義如下:

def(Bytecodes::_ireturn             , ____|disp|clvm|____, itos, itos, _return             , itos         );
def(Bytecodes::_lreturn             , ____|disp|clvm|____, ltos, ltos, _return             , ltos         );
def(Bytecodes::_freturn             , ____|disp|clvm|____, ftos, ftos, _return             , ftos         );
def(Bytecodes::_dreturn             , ____|disp|clvm|____, dtos, dtos, _return             , dtos         );
def(Bytecodes::_areturn             , ____|disp|clvm|____, atos, atos, _return             , atos         );
def(Bytecodes::_return              , ____|disp|clvm|____, vtos, vtos, _return             , vtos         );

def(Bytecodes::_return_register_finalizer , ____|disp|clvm|____, vtos, vtos, _return       ,  vtos        );

生成函數都為TemplateTable::_return()。但是如果是Object對象的構造方法中的return指令,那么這個指令還可能會被重寫為_return_register_finalizer指令。

生成的return字節碼指令對應的匯編代碼如下: 

第1部分: 

// 將JavaThread::do_not_unlock_if_synchronized屬性存儲到%dl中
0x00007fffe101b770: mov    0x2ad(%r15),%dl
// 重置JavaThread::do_not_unlock_if_synchronized屬性值為false
0x00007fffe101b777: movb   $0x0,0x2ad(%r15)

// 將Method*加載到%rbx中
0x00007fffe101b77f: mov    -0x18(%rbp),%rbx
// 將Method::_access_flags加載到%ecx中
0x00007fffe101b783: mov    0x28(%rbx),%ecx
// 檢查Method::flags是否包含JVM_ACC_SYNCHRONIZED
0x00007fffe101b786: test   $0x20,%ecx
// 如果方法不是同步方法,跳轉到----unlocked----
0x00007fffe101b78c: je     0x00007fffe101b970


// 如果在%dl寄存器中存儲的_do_not_unlock_if_synchronized的值不為0,
// 則跳轉到no_unlock,表示不要釋放和鎖相關的資源 
0x00007fffe101b792: test $0xff,%dl 
0x00007fffe101b795: jne 
0x00007fffe101ba90 // 跳轉到----no_unlock----處

在JavaThread類中定義了一個屬性_do_not_unlock_if_synchronized,這個值表示在拋出異常的情況下不要釋放receiver(在非靜態方法調用的情況下,我們總是會將方法解析到某個對象上,這個對象就是這里的receiver,也可稱為接收者),此值僅在解釋執行的情況下才會起作用。初始的時候會初始化為false。在如上匯編中可以看到,當_do_not_unlock_if_synchronized的值為true時,表示不需要釋放receiver,所以雖然當前是同步方法,但是卻直接調用到了no_unlock處。

在InterpreterGenerator::generate_native_entry()函數和InterpreterGenerator::generate_normal_entry()函數中會將當前線程的do_not_unlock_if_synchronized設置為true,然后執行increment invocation count & check for overflow的邏輯,最后會將do_not_unlock_if_synchronized屬性的值設置為false。

有如下的類定義:

class UnlockFlagSaver {
  private:
    JavaThread*   _thread;
    bool         _do_not_unlock;
  public:
    UnlockFlagSaver(JavaThread* t) {
      _thread = t;
      _do_not_unlock = t->do_not_unlock_if_synchronized();
      t->set_do_not_unlock_if_synchronized(false);
    }
    ~UnlockFlagSaver() {
      _thread->set_do_not_unlock_if_synchronized(_do_not_unlock);
    }
};

如果使用這個類,則JavaThread::_do_not_unlock_if_synchronized的值設置為false,完成后就恢復為以前的值。

在InterpreterRuntime::profile_method()函數中使用了UnlockFlagSaver,如下:

// use UnlockFlagSaver to clear and restore the _do_not_unlock_if_synchronized
// flag, in case 如果 this method triggers classloading which will call into Java.
UnlockFlagSaver fs(thread);

在InterpreterRuntime::exception_handler_for_exception()函數中,當do_not_unlock_if_synchronized的值為true時,會返回TemplateInterpreter::_remove_activation_entry。

只有TemplateInterpreterGenerator::generate_throw_exception() 函數才會調用InterpreterRuntime::exception_handler_for_exception()函數,生成的匯編由Interpreter::_rethrow_exception_entry和Interpreter::_throw_exception_entry等屬性值保存。 

第2部分:

如果執行如下匯編代碼,則表示%dl寄存器中存儲的_do_not_unlock_if_synchronized的值為0,需要執行釋放鎖的操作。

// 將之前字節碼指令執行的結果存儲到表達式棧頂,
// 由於return不需要返回執行結果,所以不需要設置返回值等信息,
// 最終在這里沒有生成任何push指令

// 將BasicObjectLock存儲到%rsi中,由於%rsi在調用C++函數時可做為
// 第2個參數傳遞,所以如果要調用unlock_object就可以傳遞此值
0x00007fffe101b79b: lea -0x50(%rbp),%rsi

// 獲取BasicObjectLock::obj屬性地址存儲到%rax中
0x00007fffe101b79f: mov 0x8(%rsi),%rax 

// 如果不為0,則跳轉到unlock處,因為不為0,表示
// 這個obj有指向的鎖對象,需要進行釋放鎖的操作
0x00007fffe101b7a3: test %rax,%rax
0x00007fffe101b7a6: jne 0x00007fffe101b8a8  // 跳轉到----unlock----處

// 如果是其它的return指令,則由於之前通過push指令將結果保存在
// 表達式棧上,所以現在可通過pop將表達式棧上的結果彈出到對應寄存器中

第1個指令的-0x50(%rbp)指向了第1個BasicObjectLock對象,其中的sizeof(BasicObjectLock)的值為16,也就是16個字節。在之前我們介紹棧幀的時候介紹過Java解釋棧的結構,如下:

 

假設當前的棧幀中有2個鎖對象,則會在棧幀中存儲2個BasicObjectLock對象,BasicObjectLock中有2個屬性,_lock和_obj,分別占用8字節。布局如下圖所示。

 

由於return字節碼指令負責要釋放的是加synchronized關鍵字的、解釋執行的Java方法,所以為synchronized關鍵字建立的第1個鎖對象存儲在離當前棧幀最靠近棧底的地方,也就是上圖中灰色部分,而其它鎖對象我們暫時不用管。灰色部分表示的BasicObjectLock的地址通過-0x50(%rbp)就能獲取到,然后對其中的_lock和_obj屬性進行操作。

由於現在還沒有介紹鎖相關的知識,所以這里不做過多介紹,在后面介紹完鎖相關知識后還會詳細介紹。 

第3部分:

在變量throw_monitor_exception為true的情況下,通過調用call_VM()函數生成拋出鎖狀態異常的匯編代碼,這些匯編代碼主要是為了執行C++函數InterpreterRuntime::throw_illegal_monitor_state_exception()。完成執行后還會執行由should_not_reach_here()函數生成的匯編代碼。

在變量throw_monitor_exception為false並且install_monitor_exception為true的情況下,通過調用call_VM()函數生成匯編代碼來執行C++函數InterpreterRuntime::new_illegal_monitor_state_exception()。最后跳轉到unlocked處執行。

第4部分:

在InterpreterMacroAssembler::remove_activation()函數中,bind完unlock后就會調用InterpreterMacroAssembler::unlock_object()函數生成如下的匯編代碼。InterpreterMacroAssembler::unlock_object()函數的作用如下:

Unlocks an object. Used in monitorexit bytecode and remove_activation. Throws an IllegalMonitorException if object is not locked by current thread.

生成的匯編代碼如下:

// **** unlock ****

// ============調用InterpreterMacroAssembler::unlock_object()函數生成如下的匯編代碼==================

// 將%r13存儲到棧中,防止異常破壞了%r13寄存器中的值
0x00007fffe101b8a8: mov %r13,-0x38(%rbp)

// 將BasicObjectLock::_lock的地址存儲到%rax寄存器中
0x00007fffe101b8ac: lea (%rsi),%rax
// 將BasicObjectLock::_obj存儲到%rcx寄存器中
0x00007fffe101b8af: mov 0x8(%rsi),%rcx

// 將BasicObjectLock::_obj的值設置為NULL,表示釋放鎖操作
0x00007fffe101b8b3: movq $0x0,0x8(%rsi)

// ----------當UseBiasedLocking的值為true時,調用MacroAssembler::biased_locking_exit()生成如下的匯編代碼------------
// 從BasicObjectLock::_obj對象中取出mark屬性值並相與
0x00007fffe101b8bb: mov (%rcx),%rdx
0x00007fffe101b8be: and $0x7,%rdx
// 如果BasicObjectLock::_obj指向的oop的mark屬性后3位是偏向鎖的狀態,則跳轉到---- done ----
0x00007fffe101b8c2: cmp $0x5,%rdx
0x00007fffe101b8c6: je 0x00007fffe101b96c
// ------------------------結束調用MacroAssembler::biased_locking_exit()生成的匯編代碼---------------------

// 將BasicObjectLock::_lock這個oop對象的_displaced_header屬性值取出
0x00007fffe101b8cc: mov (%rax),%rdx
// 判斷一下是否為鎖的重入,如果是鎖的重入,則跳轉到---- done ----
0x00007fffe101b8cf: test %rdx,%rdx
0x00007fffe101b8d2: je 0x00007fffe101b96c

// 讓BasicObjectLock::_obj的那個oop的mark恢復為
// BasicObjectLock::_lock中保存的原對象頭
0x00007fffe101b8d8: lock cmpxchg %rdx,(%rcx)
// 如果為0,則表示鎖的重入,跳轉到---- done ---- ????
0x00007fffe101b8dd: je 0x00007fffe101b96c

// 讓BasicObjectLock::_obj指向oop,這個oop的對象頭已經替換為了BasicObjectLock::_lock中保存的對象頭
0x00007fffe101b8e3: mov %rcx,0x8(%rsi)

// -----------調用call_VM()函數生成匯編代碼來執行C++函數InterpreterRuntime::monitorexit()----------------
0x00007fffe101b8e7: callq 0x00007fffe101b8f1
0x00007fffe101b8ec: jmpq 0x00007fffe101b96c
0x00007fffe101b8f1: lea 0x8(%rsp),%rax
0x00007fffe101b8f6: mov %r13,-0x38(%rbp)
0x00007fffe101b8fa: mov %r15,%rdi
0x00007fffe101b8fd: mov %rbp,0x200(%r15)
0x00007fffe101b904: mov %rax,0x1f0(%r15)
0x00007fffe101b90b: test $0xf,%esp
0x00007fffe101b911: je 0x00007fffe101b929
0x00007fffe101b917: sub $0x8,%rsp
0x00007fffe101b91b: callq 0x00007ffff66b3d22
0x00007fffe101b920: add $0x8,%rsp
0x00007fffe101b924: jmpq 0x00007fffe101b92e
0x00007fffe101b929: callq 0x00007ffff66b3d22
0x00007fffe101b92e: movabs $0x0,%r10
0x00007fffe101b938: mov %r10,0x1f0(%r15)
0x00007fffe101b93f: movabs $0x0,%r10
0x00007fffe101b949: mov %r10,0x200(%r15)
0x00007fffe101b950: cmpq $0x0,0x8(%r15)
0x00007fffe101b958: je 0x00007fffe101b963
0x00007fffe101b95e: jmpq 0x00007fffe1000420
0x00007fffe101b963: mov -0x38(%rbp),%r13
0x00007fffe101b967: mov -0x30(%rbp),%r14
0x00007fffe101b96b: retq 
// ------------------------結束call_VM()函數調用生成的匯編代碼--------------------------------

// **** done ****

0x00007fffe101b96c: mov -0x38(%rbp),%r13
0x00007fffe101b970: mov -0x40(%rbp),%rsi

// ==========結束調用InterpreterMacroAssembler::unlock_object()函數生成如下的匯編代碼============

第5部分: 

// 如果是其它的return指令,則由於之前通過push指令將結果保存在
// 表達式棧上,所以現在可通過pop將表達式棧上的結果彈出到對應寄存器中


// **** unlocked ****
// 在執行這里的代碼時,表示當前的棧中沒有相關的鎖,也就是
// 相關的鎖對象已經全部釋放

// **** restart ****
// 檢查一下,是否所有的鎖都已經釋放了

// %rsi指向當前棧中最靠棧頂的BasicObjectLock
0x00007fffe101b970: mov    -0x40(%rbp),%rsi
// %rbx指向當前棧中最靠棧底的BasicObjectLock
0x00007fffe101b974: lea    -0x40(%rbp),%rbx

// 跳轉到----entry----
0x00007fffe101b978: jmpq   0x00007fffe101ba8b

第6部分:

執行如下代碼,會通過調用call_VM()函數來生成調用InterpreterRuntime::throw_illegal_monitor_state_exception()函數的代碼:

// **** exception ****
// Entry already locked, need to throw exception

// 當throw_monitor_exception的值為true時,執行如下2個函數生成的匯編代碼:
// 執行call_VM()函數生成的匯編代碼,就是調用C++函數InterpreterRuntime::throw_illegal_monitor_state_exception()
// 執行should_not_reach_here()函數生成的匯編代碼  

// 當throw_monitor_exception的值為false,執行如下匯編:
// 執行調用InterpreterMacroAssembler::unlock_object()函數生成的匯編代碼
// install_monitor_exception的值為true時,執行call_VM()函數生成的匯編代碼,就是調用C++函數InterpreterRuntime::new_illegal_monitor_state_exception() 
// 無條件跳轉到----restart ----

第7部分:

// **** loop ****

// 將BasicObjectLock::obj與NULL比較,如果不相等,則跳轉到----exception----
0x00007fffe101ba79: cmpq   $0x0,0x8(%rsi)
0x00007fffe101ba81: jne    0x00007fffe101b97d // 則跳轉到----exception----

第8部分:

// **** entry ****

// 0x10為BasicObjectLock,找到下一個BasicObjectLock
0x00007fffe101ba87: add    $0x10,%rsi 
// 檢查是否到達了鎖對象存儲區域的底部
0x00007fffe101ba8b: cmp    %rbx,%rsi
// 如果不相等,跳轉到loop
0x00007fffe101ba8e: jne    0x00007fffe101ba79   // 跳轉到----loop----

第9部分:  

// **** no_unlock ****

// 省略jvmti support
 
// 將-0x8(%rbp)處保存的old stack pointer(saved rsp)取出來放到%rbx中
0x00007fffe101bac7: mov    -0x8(%rbp),%rbx
// 移除棧幀 // leave指令相當於: // mov %rbp, %rsp // pop %rbp 0x00007fffe101bacb: leaveq // 將返回地址彈出到%r13中 0x00007fffe101bacc: pop %r13 // 設置%rsp為調用者的棧頂值 0x00007fffe101bace: mov %rbx,%rsp 0x00007fffe101bad1: jmpq *%r13

其中的解釋方法返回地址為return address,由於當前是C++函數調用Java,所以這個返回地址其實是C++函數的返回地址,我們不需要考慮。

整個的調用轉換如下圖所示。

 

其中的紅色部分表示終結這個流程。 

在return字節碼指令中會涉及到鎖釋放的流程,所以上面的流程圖看起來會復雜一些,等我們介紹完鎖相關知識后會再次介紹return指令,這里不再過多介紹。

公眾號 深入剖析Java虛擬機HotSpot 已經更新虛擬機源代碼剖析相關文章到60+,歡迎關注,如果有任何問題,可加作者微信,拉你入虛擬機群交流

 

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM