方法返回的字节码相关指令如下表所示。

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

 

  

第36篇-return字节码指令的更多相关文章

  1. 第32篇-解析interfacevirtual字节码指令

    在前面介绍invokevirtual指令时,如果判断出ConstantPoolCacheEntry中的_indices字段的_f2属性的值为空,则认为调用的目标方法没有连接,也就是没有向Constan ...

  2. 第34篇-解析invokeinterface字节码指令

    与invokevirtual指令类似,当没有对目标方法进行解析时,需要调用LinkResolver::resolve_invoke()函数进行解析,这个函数会调用其它一些函数完成方法的解析,如下图所示 ...

  3. invokedynamic字节码指令

    1. 方法引用和invokedynamic invokedynamic是jvm指令集里面最复杂的一条.本文将从高观点的角度下分析invokedynamic指令是如何实现方法引用(Method refe ...

  4. [四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式

      前言简介   前文已经对虚拟机进行过了简单的介绍,并且也对class文件结构,以及字节码指令进行了详尽的说明 想要了解JVM的运行机制,以及如何优化你的代码,你还需要了解一下,java编译器到底是 ...

  5. 大话+图说:Java字节码指令——只为让你懂

    前言 随着Java开发技术不断被推到新的高度,对于Java程序员来讲越来越需要具备对更深入的基础性技术的理解,比如Java字节码指令.不然,可能很难深入理解一些时下的新框架.新技术,盲目一味追新也会越 ...

  6. 从字节码指令看重写在JVM中的实现

    Java是解释执行的.包含动态链接的特性.都给解析或执行期间提供了非常多灵活扩展的空间.面向对象语言的继承.封装和多态的特性,在JVM中是怎样进行编译.解析,以及通过字节码指令怎样确定方法调用的版本号 ...

  7. java虚拟机(十四)--字节码指令

    字节码指令其实是很重要的,在之前学习String等内容,深入到字节码层面很容易找到答案,而不是只是在网上寻找答案,还有可能是错误的. PS:本文基于jdk1.8 首先写个简单的类: public cl ...

  8. 硬核万字长文,深入理解 Java 字节码指令(建议收藏)

    Java 字节码指令是 JVM 体系中非常难啃的一块硬骨头,我估计有些读者会有这样的疑惑,"Java 字节码难学吗?我能不能学会啊?" 讲良心话,不是我谦虚,一开始学 Java 字 ...

  9. 【JVM源码解析】模板解释器解释执行Java字节码指令(上)

    本文由HeapDump性能社区首席讲师鸠摩(马智)授权整理发布 第17章-x86-64寄存器 不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Archit ...

随机推荐

  1. Django学习day12随堂笔记

    每日测验 """ 1.什么是cookie和session,你能描述一下它们的由来和工作机制吗(切勿糊弄,敷衍了事) 2.django中如何操作cookie和session ...

  2. Java从入门到精通(第5版)上半部分

    1.1java简介 先起了oak 橡树 这个名字,因为商标原因改为爪洼岛谐音的Java 一次编写,到处运行 java如何运行 java程序既是编译型又是解释型 Java版本 Java SE 标准版(开 ...

  3. ecshop首页调用团购说明

    要在首页调用购买. 发现在首页还不能直接调用团购说明.查看了一下代码发现要修改下才能调 打开根目录的 index.php 文件找到 $sql = 'SELECT gb.act_id AS group_ ...

  4. thinkphp5.0框架运行机制分享小结

    1 访问index.php 入口文件,定义应用目录,加载框架引导文件 <?php // [ 应用入口文件 ] // 定义应用目录 define('APP_PATH', __DIR__ . '/. ...

  5. 『Python』列表生成式、生成器与迭代器

    1. 迭代 在 Python中, 迭代是通过 for ... in 来完成的, 而很多语言比如 C 语言, 迭代 list 是通过下标完成的. Python 的 for 循环抽象程度要高于 C 的 f ...

  6. 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 百篇博客分析OpenHarmony源码 | v13.02

    百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...

  7. centos7.5离线安装Docker及容器运行报OCI runtime create failed 问题定位与解决

    前言 接上篇 <记一次centos挂载ceph存储的坑> 服务器重做了centos7.5版本的操作系统,剩下就是安装docker,考虑yum安装耗时较长,我一般都是直接安装二进制版本doc ...

  8. RabbitMQ 3.9.7 镜像模式集群的搭建

    1. 概述 老话说的好:做人脚踏实地,一步一个脚印,便定能战胜一切困难,最终取得成功!!! 言归正传,之前我们聊了 RabbitMQ 单点服务的安装,今天我们来聊聊 RabbitMQ 3.9.7 镜像 ...

  9. 使用CEF(一)— 起步

    使用CEF(一)- 起步 介绍 Chromium Embedded Framework (CEF)是个基于Google Chromium项目的开源Web browser控件,支持Windows, Li ...

  10. 从零入门 Serverless | Knative 带来的极致 Serverless 体验

    作者 | 冬岛 阿里巴巴高级技术专家 Serverless 公众号后台回复"knative",即可免费下载<Knative 云原生应用开发指南>电子书! 导读:Serv ...