generate_fixed_frame()方法生成Java方法栈帧
在从generate_normal_entry()函数调用generate_fixed_frame()函数时的栈与寄存器的状态如下:
栈的状态如下图所示。

各个寄存器的状态如下所示。
rax: return address // %rax寄存器中存储的是返回地址r
rbx: Method* // 要执行的Java方法的指针
r14: pointer to locals // 本地变量表指针
r13: sender sp // 调用者的栈顶
调用的generate_fixed_frame()方法的实现如下:
源代码位置:src/cpu/x86/vm/templateInterpreter_x86_64.cpp // Generate a fixed interpreter frame. This is identical setup for
// interpreted methods and for native methods hence the shared code.
void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) {
// initialize fixed part of activation frame
__ push(rax); // save return address 把返回地址紧接着局部变量区保存
__ enter(); // save old & set new rbp 进入固定桢
__ push(r13); // set sender sp 保存调用者的栈顶地址
__ push((int)NULL_WORD); // leave last_sp as null
__ movptr(r13, Address(rbx, Method::const_offset())); // 获取ConstMethod*并保存到r13中
__ lea(r13, Address(r13, ConstMethod::codes_offset())); // 保存字节码的地址到r13中
__ push(rbx); // 保存Method*的地址到堆栈上 // ProfileInterpreter的默认值为true,表示需要对方法进行信息编译
if (ProfileInterpreter) {
Label method_data_continue;
// MethodData结构基础是ProfileData,记录函数运行状态下的数据
// MethodData里面分为3个部分,一个是函数类型等运行相关统计数据,一个是参数类型运行相关统计数据,
// 还有一个是extra扩展区保存着deoptimization的相关信息
// 获取Method中的_method_data属性的值并保存到rdx中
__ movptr(rdx, Address(rbx, in_bytes(Method::method_data_offset())));
__ testptr(rdx, rdx);
__ jcc(Assembler::zero, method_data_continue);
// 执行到这里,说明_method_data已经进行了初始化,通过MethodData来获取_data属性的值并存储到rdx中
__ addptr(rdx, in_bytes(MethodData::data_offset()));
__ bind(method_data_continue);
__ push(rdx); // set the mdp (method data pointer)
} else {
__ push(0);
} __ movptr(rdx, Address(rbx, Method::const_offset())); // 获取ConstMethod*存储到rdx
__ movptr(rdx, Address(rdx, ConstMethod::constants_offset())); // 获取ConstantPool*存储到rdx
// 获取ConstantPoolCache*并存储到rdx
__ movptr(rdx, Address(rdx, ConstantPool::cache_offset_in_bytes()));
__ push(rdx); // 保存ConstantPoolCache*到堆栈上
__ push(r14); // 保存第1个参数的地址到堆栈上 if (native_call) {
__ push(0); // no bcp
} else {
__ push(r13); // 保存Java方法字节码地址到堆栈上,注意上面对r13寄存器的值进行了更改
}
__ push(0); // reserve word for pointer to expression stack bottom
__ movptr(Address(rsp, 0), rsp); // set expression stack bottom //在rsp的地址保存rsp的值
}
生成的汇编代码如下:
0x00007fffed01b254: push %rax
0x00007fffed01b255: push %rbp
0x00007fffed01b256: mov %rsp,%rbp
0x00007fffed01b259: push %r13
0x00007fffed01b25b: pushq $0x0
0x00007fffed01b260: mov 0x10(%rbx),%r13
0x00007fffed01b264: lea 0x30(%r13),%r13 // lea指令获取内存地址本身
0x00007fffed01b268: push %rbx
0x00007fffed01b269: mov 0x18(%rbx),%rdx
0x00007fffed01b26d: test %rdx,%rdx
0x00007fffed01b270: je 0x00007fffed01b27d
0x00007fffed01b276: add $0x90,%rdx
0x00007fffed01b27d: push %rdx
0x00007fffed01b27e: mov 0x10(%rbx),%rdx
0x00007fffed01b282: mov 0x8(%rdx),%rdx
0x00007fffed01b286: mov 0x18(%rdx),%rdx
0x00007fffed01b28a: push %rdx
0x00007fffed01b28b: push %r14
0x00007fffed01b28d: push %r13
0x00007fffed01b28f: pushq $0x0
0x00007fffed01b294: mov %rsp,(%rsp)
通过源代码结合汇编的方式可以将代码的逻辑看的清清楚楚,最终的栈状态变为了如下的样子:

左边的栈底与右边的栈顶是连续的,右边的栈中除local variable1、...、local variable n之外都是调用generate_fixed_frame()生成的。而argument word 1、...、argument word n加上右边的栈布局就是调用Java方法的栈布局,也就是如下图所示。

可以看到,2个栈帧重用了argument word 1、...、argument word n,而浅灰色与深灰色加起来就是为调用Java方法分配的本地变量表。
相关文章的链接如下:
1、在Ubuntu 16.04上编译OpenJDK8的源代码
13、类加载器
14、类的双亲委派机制
15、核心类的预装载
16、Java主类的装载
17、触发类的装载
18、类文件介绍
19、文件流
20、解析Class文件
21、常量池解析(1)
22、常量池解析(2)
23、字段解析(1)
24、字段解析之伪共享(2)
25、字段解析(3)
28、方法解析
29、klassVtable与klassItable类的介绍
30、计算vtable的大小
31、计算itable的大小
32、解析Class文件之创建InstanceKlass对象
33、字段解析之字段注入
34、类的连接
35、类的连接之验证
36、类的连接之重写(1)
37、类的连接之重写(2)
38、方法的连接
39、初始化vtable
40、初始化itable
41、类的初始化
42、对象的创建
43、Java引用类型
50、CallStub栈帧
作者持续维护的个人博客 classloading.com。
关注公众号,有HotSpot源码剖析系列文章!
generate_fixed_frame()方法生成Java方法栈帧的更多相关文章
- 第5篇-调用Java方法后弹出栈帧及处理返回结果
在前一篇 第4篇-JVM终于开始调用Java主类的main()方法啦 介绍了通过callq调用entry point,不过我们并没有看完generate_call_stub()函数的实现.接下来在ge ...
- 第6篇-Java方法新栈帧的创建
在 第2篇-JVM虚拟机这样来调用Java主类的main()方法 介绍JavaCalls::call_helper()函数的实现时提到过如下一句代码: address entry_point = me ...
- 第7篇-为Java方法创建栈帧
在 第6篇-Java方法新栈帧的创建 介绍过局部变量表的创建,创建完成后的栈帧状态如下图所示. 各个寄存器的状态如下所示. // %rax寄存器中存储的是返回地址 rax: return addres ...
- 牛客网Java刷题知识点之内存的划分(寄存器、本地方法区、方法区、栈内存和堆内存)
不多说,直接上干货! 其中 1)程序计数器:用于指示当前线程所执行的字节码执行到了第几行,可以理解为当前线程的行号指示器.每个计数器志勇赖记录一个线程的行号,所以它是线程私有的. ...
- java方法的理解、调用栈与异常处理
一.流程分支 If/else :基于boolean值的双分支 Switch:基于数字(整数.char.byte.枚举).字符串 类型的多分支 Int month =5; Switch 二.方法meth ...
- Java虚拟机栈 和 方法区 的联系
1.Java虚拟机栈 java方法执行时的内存模型 1.1 栈帧 每个方法都会在虚拟机栈中创建一个对应的栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息. 一个方法的调用到结束就对应这一个 ...
- JVM 运行时数据区:程序计数器、Java 虚拟机栈和本地方法栈,方法区、堆以及直接内存
Java 虚拟机可以看作一台抽象的计算机,如同真实的计算机,它也有自己的指令集和运行时内存区域. Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存(运行时内存区域)划分为若干个不同的数 ...
- JAVA栈帧
简介 Java栈是一块线程私有的内存空间.java堆和程序数据相关,java栈就是和线程执行密切相关的,线程的执行的基本行为是函数调用,每次函数调用的数据都是通过java栈来传递的. Java栈与数据 ...
- Java虚拟机运行时栈帧结构--《深入理解Java虚拟机》学习笔记及个人理解(二)
Java虚拟机运行时栈帧结构(周志明书上P237页) 栈帧是什么? 栈帧是一种数据结构,用于虚拟机进行方法的调用和执行. 栈帧是虚拟机栈的栈元素,也就是入栈和出栈的一个单元. 2018.1.2更新(在 ...
随机推荐
- Oracle对表进行备份
前言: 在实际开发中,我们常常需要对单张或多张表进行备份,以下博主就从这两个方面进行总结.如需转载,请标明来处,谢谢! 在备份前我们先创建表盒相关测试的数据 -- Create table creat ...
- twitch游戏直播(【国外】平台)如何绑定二次验证码_虚拟MFA?
一般点账户名——设置——安全设置中开通虚拟MFA两步验证 具体步骤见链接 twitch游戏直播([国外]平台)如何绑定二次验证码_虚拟MFA? 二次验证码小程序于谷歌身份验证器APP的优势(更多见官网 ...
- jsp课堂笔记4 javabean
Javabean是一个可重复使用的软件组件,实际上是一种java类 实现代码重复利用 易编写易维护易使用 jsp页面的主要任务是显示页面,不负责数据的逻辑业务处理 将数据处理过程中指派一个或多个bea ...
- 番外:socketserver用法
进击のpython ***** 番外:socketserver使用 是不是被一般写法,多进程写法,多线程写法甚至是协程写法搞的不可开交 云里雾里,仿佛将要放弃~再配上服务器要服务多个客户端 完蛋了,全 ...
- ubuntu 12.04无盘工作站
注释:该篇博文是借鉴下列文章加上自己实践总结得来: a. http://forum.ubuntu.org.cn/viewtopic.php?f=77&t=117754 b. http://bl ...
- python学习之路------你想要的都在这里了
python学习之路------你想要的都在这里了 (根据自己的学习进度后期不断更新哟!!!) 一.python基础 1.python基础--python基本知识.七大数据类型等 2.python基础 ...
- 设置x 轴斜体(每次我都百度,这次单独为它发一个)
plt.xticks(rotation = 45) 2020-06-07
- PHP exp() 函数
实例 返回 'e' 的不同次方: <?phpecho(exp(0) . "<br>");echo(exp(1) . "<br>") ...
- qemu 如何退出qemu
如何退出qemu ctrl + a 放开后,再按下x 这里字母是小写!(不要按着大写键)
- 5.12 省选模拟赛 T2 贪心 dp 搜索 差分
LINK:T2 这题感觉很套路 但是不会写. 区间操作 显然直接使用dp不太行 直接爆搜也不太行复杂度太高. 容易想到差分 由于使得整个序列都为0 那么第一个数也要i差分前一个数 强行加一个0 然后 ...