• 第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+,欢迎关注,如果有任何问题,可加作者微信,拉你入虚拟机群交流

     

      

  • 相关阅读:
    POJ 1862 Stripies (简单贪心)
    HDU 2181 哈密顿绕行世界问题(DFS)
    POJ 3262 Protecting the Flowers (贪心)
    Aizu 2249 Road Construction (最短路 SPFA)
    POJ 3484 Showstopper(二分)
    HDU 6235 2017-CCPC-哈尔滨站 Permutation(简单模拟)
    IDEA常用插件
    解决IDEA插件加载失败
    解决zsh无法使用maven
    Linux安装Maven
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/15502700.html
Copyright © 2020-2023  润新知