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更新(在 ...
随机推荐
- 001.Nginx简介
一 Nginx概述 1.1 Nginx简介 Nginx是一个高性能的HTTP和反向代理web服务器,Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在B ...
- Linux 终端最全推荐(建议收藏)
本文来自网络整理,如有侵权,则可删除. 如果你跟我一样,整天要花大量的时间使用Linux命令行,而且正在寻找一些可替代系统自带的老旧且乏味的终端软件,那你真是找对了文章.我这里搜集了一些非常有趣的终端 ...
- WebApiClientCore简约调用百度AI接口
WebApiClientCore WebApiClient.JIT/AOT的netcore版本,集高性能高可扩展性于一体的声明式http客户端库,特别适用于微服务的restful资源请求,也适用于各种 ...
- el-table 操作列(编辑or删除) 获取本行相关数据
简单说明:开发的时候,经常会遇到表格后面跟着操作列,一般都是编辑或者删除,那么 就需要获取到 本行数据相关的id或者其他附属信息.ok,下边放代码 //vue el-table的部分代码 <el ...
- SpringBoot-使用lombok插件运行报错
SpringBoot-使用lombok插件运行报错 标签(空格分隔): java,SpringBoot 1.报错信息 2.解决方案 1.IDEA编辑器安装lombok插件 2.编译注解配置-Enabl ...
- Python游戏编程入门 中文pdf扫描版|网盘下载内附地址提取码|
Python是一种解释型.面向对象.动态数据类型的程序设计语言,在游戏开发领域,Python也得到越来越广泛的应用,并由此受到重视. 本书教授用Python开发精彩游戏所需的[]为重要的该你那.本书不 ...
- PHP gd_info - 取得当前安装的 GD 库的信息
gd_info — 取得当前安装的 GD 库的信息. 语法 array gd_info ( void )高佣联盟 www.cgewang.com 返回一个关联数组描述了安装的 GD 库的版本和性能. ...
- FFT专练
就多项式乘法这个地方不太熟 再多巩固一下. LINK:[ZJOI2014力](https://www.luogu.com.cn/problem/P3338) 把$(j-i)^2$看成一个函数 可以发现 ...
- C语言中的数据转换和定义常量
一.数据转换 1.数据类型转换:C 语言中如果一个表达式中含有不同类型的常量和变量,在计算时,会将它们自动转换为同一种类型:在 C 语言中也可以对数据类型进行强制转换: 2.自动转换规则: a)浮点数 ...
- Android Studio中如何使用自定义的framework库
在安卓app开发中,通常不会遇到需要使用自定义framework库的情况,使用的都是标准的内核库.但也有例外,比如针对定制化的ROM,ROM厂商可能在ROM中对安卓源码做过修改,对应用层app暴露出与 ...