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更新(在 ...
随机推荐
- Dubbo的负载均衡算法源码分析
Dubbo提供了四种负载均衡:RandomLoadBalance,RoundRobinLoadBalance,LeastActiveLoadBalance,ConsistentHashLoadBala ...
- 在Ubuntu 18.04中安装Wine QQ、微信、TIM
近日重新安装了Ubuntu 18.04,因此要重新安装一下Wine QQ.微信之类的,完整安装Wine系列软件一直是一个老大难的问题,网上搜集到的博客也比较零散,因此这里特此写篇博客记录一下 0. 这 ...
- 全卷积神经网络FCN详解(附带Tensorflow详解代码实现)
一.导论 在图像语义分割领域,困扰了计算机科学家很多年的一个问题则是我们如何才能将我们感兴趣的对象和不感兴趣的对象分别分割开来呢?比如我们有一只小猫的图片,怎样才能够通过计算机自己对图像进行识别达到将 ...
- 第九章 Lambda&方法引用
9.1.Lambda表达式 9.1.1.标准格式 (形式参数) -> {代码块} 9.1.2.使用前提 有一个接口并且接口中有且仅有一个抽象方法 9.1.3.常见应用 9.1.3.1.无参无返回 ...
- 使用Spring Validation优雅地校验参数
写得好的没我写得全,写得全的没我写得好 引言 不知道大家平时的业务开发过程中 controller 层的参数校验都是怎么写的?是否也存在下面这样的直接判断? public String add(Use ...
- 设置x 轴斜体(每次我都百度,这次单独为它发一个)
plt.xticks(rotation = 45) 2020-06-07
- PHP unserialize() 函数
unserialize() 函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构. PHP 版本要求: PHP 4, PHP 5, PHP 7高佣联盟 w ...
- Nginx使用中遇到的问题记录
问题一.关于空格 nginx配置对空格十分敏感,在关键字和符号的前后,一定记得有空格(或换行).一个典型的场景是 if { } 语句,大括号前后要有空格,否则可能出现非预期行为. 问题二.关于serv ...
- SqlServer 查询的几种方式以及数字函数、时间函数的应用总结(回归基础)
--语法:select * from 表名 *表示查询所有字段数据 select * from Class select * from Student select * from RankingLis ...
- 系统UISearchController详解
原文链接:https://www.jianshu.com/p/aa9a153a5b58