在TemplateInterpreterGenerator::generate_all()函数中生成了许多字节码指令以及一些虚拟机辅助执行的机器指令片段,例如生成空指针异常抛出入口的实现如下:

{
CodeletMark cm(_masm, "throw exception entrypoints");
// ...
Interpreter::_throw_NullPointerException_entry = generate_exception_handler("java/lang/NullPointerException",NULL);
// ...
}

调用generate_exception_handler()函数生成抛出空指针的代码片段。

address generate_exception_handler(const char* name, const char* message) {
return generate_exception_handler_common(name, message, false);
}

调用的generate_exception_handler_common()函数的实现如下:

address TemplateInterpreterGenerator::generate_exception_handler_common(
const char* name,
const char* message,
bool pass_oop
) { assert(!pass_oop || message == NULL, "either oop or message but not both");
address entry = __ pc();
if (pass_oop) {
// object is at TOS
__ pop(c_rarg2);
} // expression stack must be empty before entering the VM if an
// exception happened
__ empty_expression_stack(); // setup parameters
__ lea(c_rarg1, ExternalAddress((address)name)); if (pass_oop) {
__ call_VM(rax,
CAST_FROM_FN_PTR(address,InterpreterRuntime::create_klass_exception),
c_rarg1,c_rarg2);
} else {
// kind of lame ExternalAddress can't take NULL because
// external_word_Relocation will assert.
if (message != NULL) {
__ lea(c_rarg2, ExternalAddress((address)message));
} else {
__ movptr(c_rarg2, NULL_WORD);
}
__ call_VM(rax,
CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception),
c_rarg1, c_rarg2);
} // throw exception
__ jump(ExternalAddress(Interpreter::throw_exception_entry())); return entry;
}

生成的汇编代码如下:

0x00007fffe10101cb: mov    -0x40(%rbp),%rsp
0x00007fffe10101cf: movq $0x0,-0x10(%rbp)
0x00007fffe10101d7: movabs $0x7ffff6e09878,%rsi
0x00007fffe10101e1: movabs $0x0,%rdx
0x00007fffe10101eb: callq 0x00007fffe10101f5
0x00007fffe10101f0: jmpq 0x00007fffe1010288
0x00007fffe10101f5: lea 0x8(%rsp),%rax
0x00007fffe10101fa: mov %r13,-0x38(%rbp)
0x00007fffe10101fe: mov %r15,%rdi
0x00007fffe1010201: mov %rbp,0x200(%r15)
0x00007fffe1010208: mov %rax,0x1f0(%r15)
0x00007fffe101020f: test $0xf,%esp
0x00007fffe1010215: je 0x00007fffe101022d
0x00007fffe101021b: sub $0x8,%rsp
0x00007fffe101021f: callq 0x00007ffff66b3fbc
0x00007fffe1010224: add $0x8,%rsp
0x00007fffe1010228: jmpq 0x00007fffe1010232
0x00007fffe101022d: callq 0x00007ffff66b3fbc
0x00007fffe1010232: movabs $0x0,%r10
0x00007fffe101023c: mov %r10,0x1f0(%r15)
0x00007fffe1010243: movabs $0x0,%r10
0x00007fffe101024d: mov %r10,0x200(%r15)
0x00007fffe1010254: cmpq $0x0,0x8(%r15)
0x00007fffe101025c: je 0x00007fffe1010267
0x00007fffe1010262: jmpq 0x00007fffe1000420
0x00007fffe1010267: mov 0x250(%r15),%rax
0x00007fffe101026e: movabs $0x0,%r10
0x00007fffe1010278: mov %r10,0x250(%r15)
0x00007fffe101027f: mov -0x38(%rbp),%r13
0x00007fffe1010283: mov -0x30(%rbp),%r14
0x00007fffe1010287: retq
0x00007fffe1010288: jmpq 0x00007fffe100f3d3

在这里的重点不是读懂TemplateInterpreterGenerator::generate_exception_handler_common()函数的逻辑及生成的汇编代码,而是要清楚知道CodeletMark的应用,以及generate_exception_handler_common()函数生成的机器指令是如何写入InterpreterCodelet实例中的。之前介绍过InterpreterCodelet与CodeBuffer类,如下:

通过CodeBuffer操作InterpreterCodelet实例的存储机器指令片段的内存区域,而CodeBuffer中的代码部分(CodeSection)被赋值给AbstractAssembler::_code_section。这样我们就可以通过_code_section属性向InterpreterCodelet实例中写入机器指令了。

向CodeletMark中传入的_masm参数定义在AbstractInterpreterGenerator类中,如下:

class AbstractInterpreterGenerator: public StackObj {
protected:
InterpreterMacroAssembler* _masm;
// ...
}

generate_exception_handler_common()函数中的__是一个宏,定义如下:

#define __ _masm->

这样其实就是调用InterpreterMacroAssembler类中的相关函数写机器指令,例如

__ pop(c_rarg2);

调用的pop()函数如下:

// 定义在InterpreterMacroAssembler中
void pop(Register r ) {
((MacroAssembler*)this)->pop(r);
} // 定义在Assembler类中
void Assembler::pop(Register dst) {
int encode = prefix_and_encode(dst->encoding());
emit_int8(0x58 | encode);
} // 定义在AbstractAssembler类中
void emit_int8( int8_t x) {
code_section()->emit_int8( x);
}

code_section()函数获取的就是AbstractAssembler的_code_section属性的值。  

推荐阅读:

第1篇-关于JVM运行时,开篇说的简单些

第2篇-JVM虚拟机这样来调用Java主类的main()方法

第3篇-CallStub新栈帧的创建

第4篇-JVM终于开始调用Java主类的main()方法啦

第5篇-调用Java方法后弹出栈帧及处理返回结果

第6篇-Java方法新栈帧的创建

第7篇-为Java方法创建栈帧

第8篇-dispatch_next()函数分派字节码

第9篇-字节码指令的定义

第10篇-初始化模板表

第11篇-认识Stub与StubQueue

第12篇-认识CodeletMark

如果有问题可直接评论留言或加作者微信mazhimazh

关注公众号,有HotSpot VM源码剖析系列文章!

第13篇-通过InterpreterCodelet存储机器指令片段的更多相关文章

  1. Mysql高手系列 - 第13篇:细说NULL导致的神坑,让人防不胜防

    这是Mysql系列第13篇. 环境:mysql5.7.25,cmd命令中进行演示. 当数据的值为NULL的时候,可能出现各种意想不到的效果,让人防不胜防,我们来看看NULL导致的各种神坑,如何避免? ...

  2. 3.Ceph 基础篇 - RBD 块存储使用

    文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247485253&idx=1&sn=24d9b06a ...

  3. 有评论就是我最大的动力~MySQL基础篇完结(存储引擎和图形化管理工具)

    hi 今天登上来,发现竟然有了3个评论~~加油吧! 这周的计划其实远远没有达到,然后下周还有一大堆事情...那么...周末好好玩吧~ 今天试图完结MySQL的基础篇知识,小白变为大白? 1.MySQL ...

  4. Spring第13篇—–Spring整合Hibernate之声明式事务管理

    不容置疑的我们可以知道Spring的事务管理是通过AOP(AOP把我们的事务管理织入到我们的业务逻辑里面了)的方式来实现的,因为事务方面的代码与spring的绑定并以一种样板式结构使用.(面向切面编程 ...

  5. 剖析Elasticsearch集群系列第一篇 Elasticsearch的存储模型和读写操作

    剖析Elasticsearch集群系列涵盖了当今最流行的分布式搜索引擎Elasticsearch的底层架构和原型实例. 本文是这个系列的第一篇,在本文中,我们将讨论的Elasticsearch的底层存 ...

  6. Django【第13篇】:Django之Form组件

    django 之知识点总结以及Form组件 一.model常用操作 1.13个API查询:all,filter,get ,values,values_list,distinct,order_by ,r ...

  7. C++基础知识篇:C++ 存储类

    存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期.这些说明符放置在它们所修饰的类型之前.下面列出 C++ 程序中可用的存储类: auto register static extern m ...

  8. K8S系列第九篇(持久化存储,emptyDir、hostPath、PV/PVC)

    更多k8s内容,请关注威信公众好:新猿技术生态圈 一.数据持久化 Pod是由容器组成的,而容器宕机或停止之后,数据就随之丢了,那么这也就意味着我们在做Kubernetes集群的时候就不得不考虑存储的问 ...

  9. MySQL索引篇之索引存储模型

      本文重点介绍下索引的存储模型 二分查找   给定一个1~100的自然数,给你5次机会,你能猜中这个数字吗? 你会从多少开始猜?   为什么一定是50呢?这个就是二分查找的一种思想,也叫折半查找,每 ...

随机推荐

  1. 《面试八股文》之 Redis 16卷

    微信公众号:moon聊技术 关注选择" 星标 ", 重磅干货,第一 时间送达! [如果你觉得文章对你有帮助,欢迎关注,在看,点赞,转发] 大家好,我是 moon. redis 作为 ...

  2. 网络损伤仪WANsim的功能与型号

    网络损伤仪简介 WANsim网络损伤仪是一款可以模拟真实的广域网状况的设备.它可以通过简单的数据填写模拟出时延.带宽抖动.丢包.乱序.误码.报文重复等网络状况. 大多数公司在进行产品测试时,只关注了应 ...

  3. Python自动化测试面试题-Selenium篇

    目录 Python自动化测试面试题-经验篇 Python自动化测试面试题-用例设计篇 Python自动化测试面试题-Linux篇 Python自动化测试面试题-MySQL篇 Python自动化测试面试 ...

  4. <题解>[IOI2019]景点划分

    题目传送门(luogu) 题目传送门(loj) 这个题对我来说可以算是超出了我的能力范围 被学长拿来教我做构造,构造题真简单,构造题真是人,构造题真能手切... 首先对于本题,必须要知道dfs树这东西 ...

  5. PASS-单组目标值法的样本量计算

    临床试验的参数估计中,评价指标有确定的估计目标 ,临床试验目的需通过参数估计(含相应的可信区间估计)的方法证明评价指标不低于目标值时,可根据单组目标值法样本量公式计算. 例:欲证明器械A的诊断准确性非 ...

  6. solr(CVE-2019-17558)远程命令执行

    影响版本 Apache Solr 5.x到8.2.0版本 测试 https://github.com/jas502n/CVE-2019-0193

  7. CFS靶机

    nmap扫描 nmap -sV -p1-65535 192.168.1.135 thinkphp5.0版本 找到poc进行测试 http://192.168.1.135/index.php?s=ind ...

  8. 深度强化学习中稀疏奖励问题Sparse Reward

    Sparse Reward 推荐资料 <深度强化学习中稀疏奖励问题研究综述>1 李宏毅深度强化学习Sparse Reward4 ​ 强化学习算法在被引入深度神经网络后,对大量样本的需求更加 ...

  9. Java8新特性(三)之方法引用和构造器引用

    1.使用场景 当要传递给Lambda体的操作,已经存在实现的方法了,就可以使用方法引用.(抽象方法的参数列表  必须与方法引用方法的参数列表保持一致) 2. 语法 使用操作符[::]将方法名和对象或类 ...

  10. Notes about WindowPadX

    WindowPadX乃一Autohotkey脚本,具有强大的单/多显示器窗口排布能力且易于配置.有了它,那些Pro版收费的.需要安装的DisplayFusion, MultiMon TaskBar, ...