当前位置:网站首页>卷起来,突破35岁焦虑,动画演示CPU记录函数调用过程
卷起来,突破35岁焦虑,动画演示CPU记录函数调用过程
2022-07-05 09:30:00 【hi-dhl】
hi 大家好,我是 DHL。公众号:ByteCode ,专注分享有趣硬核原创内容,Kotlin、Jetpack、性能优化、系统源码、算法及数据结构、动画、大厂面经
全文分为 视频版 和 文字版,
视频版:
通过语音和动画,能够更加直观的看到,内存记录方法调用和返回过程。
文字版
我们在写代码的时候有没有思考过 方法如何调用 、 方法执行完之后如何返回 、 内存如何记录方法调用过程 。而这也是今天这篇文章重点内容。
方法调用和返回过程涉及到了,虚拟机栈、程序计数器、局部变量表、操作数栈、方法返回地址、动态链接等等内容,涉及到知识点很多,同时这些内容也是高频面试题,所以我将拆分成多篇文章,针对每个知识点去做详细的分析。而今天这篇文章我们重点去看内存是如何记录方法调用和返回过程。
虚拟机栈
Java 方法以栈帧的形式,运行在虚拟机栈(Java 栈)中,栈是线程私有的,程序启动的时候,会创建一个 main 线程,操作系统会为每一个线程分配一段内存,线程创建的时候会创建一个虚拟机栈,虚拟机栈的生命周期和线程一样,线程结束了,虚拟机栈也销毁了。
每个 Java 方法,对应一个个栈帧,所以方法开始和结束,都是一个个栈帧入栈和出栈的过程,效果如下图所示。
栈帧
每个 Java 方法,都是一个个栈帧,每个栈帧包括了:局部变量表、操作数栈、方法返回地址、动态链接、附加信息。
- 局部变量表: 保存方法参数列表和方法内的局部变量,按照声明的顺序存储,它以数组的形式展示,如果是实例方法,索引为 0 是 this 的引用,如下图所示。
索引(Slot) | 名字(Name) |
---|---|
0 | this |
1 | num |
2 | res |
- 操作数栈: 保存方法执行过程中的临时结果
- 返回地址: 保存调用该方法的 pc 寄存器的值(即 JVM 指令地址),用于方法结束时,返回调用处,让调用者方法继续执行下去
- 动态链接: 指向运行时常量池中该栈帧所属方法的引用,即从常量池中找到目标方法的符号引用,然后转换为直接引用
附加信息:比如程序 debug 时添加的一些附件信息(不重要,不需要关心,可忽略)
这里只需要知道它们的作用即可,它们的数据结构、字节码的含义、执行过程等等,后续的文章我将会针对每个知识点去做详细的分析。
方法调用过程
先写一段方法调用的代码,首先会调用 main()
方法之后调用 fun1()
然后调用 fun2()
,如下图所示。
现在我们来演示一下 Java 虚拟机执行这些 JVM 指令的过程,首先会调用 main () 方法。
main () 方法
main()
方法执行流程动画效果如下所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ApkZcRTw-1654832156342)(https://img.hi-dhl.com/16541790180176.gif)]
- 执行指令
0: aload_0
,从局部变量表中,读取索引为 0 的值,压入操作数栈中,因为是实例方法,所以索引为 0 的值是 this 的引用
- 执行指令
1: iconst_5
,将常量 5 压入操作数栈中
- 执行指令
2: invokevirtual #7
,常量 5 和 this 从操作数栈中出栈,然后调用this.fun1(5)
首先从常量池中找到方法 fun1()
的符号引用,然后通过动态链接将符号引用转换成直接引用,之后调用 this.fun1(5)
,将方法 fun1()
作为栈帧压入虚拟机栈中,跳转到 fun1()
,继续往下执行。
如何从常量池中找到目标方法的符号引用,然后转换成直接引用的过程,将会在后面系列文章中详细分析
在调用 fun1()
之前,fun1()
的局部变量表和方法返回地址已经确定好了。
进入方法 fun1 (int num)
方法 fun1(int num)
执行流程动画效果如下所示。
- 执行指令
0: aload_0
,从局部变量表中,读取索引为 0 的值,压入操作数栈中。因为是实例方法,所以索引为 0 的值是 this 的引用
- 执行指令
1: iload_1
,从局部变量表中,读取索引为 1 变量 num 的值,并压入操作数栈
- 执行指令
2: invokevirtual #13
,num 和 this 从操作数栈中出栈,然后调用this.fun2(num)
首先从常量池中找到方法 fun2()
的符号引用,然后通过动态链接将符号引用转换成直接引用,之后调用 this.fun2(num)
,将方法 fun2()
作为栈帧压入虚拟机栈中,跳转到 fun2()
,继续往下执行,调用 fun2()
之前,fun2()
的局部变量表和方法返回地址已经确定好了。
进入方法 fun2 (int num)
方法 fun2(int num)
执行流程动画效果如下所示。
- 执行指令
0: iload_1
,从局部变量表中,读取索引为 1 变量 num 的值,并压入操作数栈中
- 执行指令
1: bipush
,将常量 10 压入操作数栈中
- 执行指令
3: iadd
,num 和常量 10 从操作数栈中出栈,然后进行相加,将结果压入操作数栈中
- 执行指令
4: istore_2
,从操作数栈中取出结果,并赋值给局部变量表中索引为 2 的变量 res2
- 执行指令
5: iload_2
,从局部变量表中,读取索引为 2 的变量 res2 的值,并压入操作数栈中
方法退出过程
每个方法即是一个栈帧,每个栈帧会保存方法返回地址,执行 return
系列指令时,会根据当前栈帧保存的返回地址,返回到调用的位置,继续往下执行。因此会分为两种情况。
异常退出
如果出现了异常且捕获了该异常,则会从异常表里查找 PC 寄存器的地址(JVM 指令地址),返回调用处继续执行。
正常退出时会做以下件事
- 恢复上一个栈帧局部变量表、操作数栈
- 如果有返回值,将返回值压入调用者栈帧的操作数栈,是否有返回值根据
return
JVM 指令:ireturn
:返回值是boolean
、byte
、char
、short
和int
类型lreturn
:返回值是Long
类型freturn
:返回值是Float
类型dreturn
:返回值是Double
类型areturn
:返回值是引用类型return
:返回值类型为void
- 设置调用者栈帧的 JVM 指令地址
- 当前栈帧从虚拟机栈中出栈
这篇文章我们只分析正常退出流程,异常退出和正常退出的流程大致都是一样的。正常退出流程动画效果如下所示。
- 方法
fun2
结束时,执行最后一条指令ireturn
,当前栈帧从虚拟机栈中出栈,根据当前栈帧保存的方法返回地址,返回到 fun1 ,恢复 fun1 的局部变量表、操作数栈,将返回结果res2
保存到调用者(fun1)操作数栈中
- 回到方法
fun1(int num)
,执行指令5: istore_2
,变量res2
从操作数中出栈,赋值给局部变量表中索引为 2 的变量res1
- 执行指令
6: iload_2
,从局部变量表中,读取索引为 2 变量res1
的值,并压入操作数栈中
- 执行方法 fun1 最后一条指令
7: ireturn
,当前栈帧从虚拟机栈中出栈,根据当前栈帧保存的方法返回地址,返回到 main 方法,恢复 main 方法的局部变量表、操作数栈,将返回结果res2
保存到调用者(main)操作数栈中
- 最后回到方法
main
中,执行最后一条指令5: pop
,操作数栈中的元素出栈
- 执行最后一条指令
6: return
,至此所有的方法调用结束了,方法所占用的内存,也将返回给系统
每次的方法调用,即是栈帧入栈和出栈的过程,同时也需要占用部分内存,用于保存局部变量表、操作数栈、方法返回地址、动态链接、附加信息等等。
当方法执行完,即栈帧出栈,方法调用所占用的内存,也将返回给系统。
全文到这里就结束了,感谢你的阅读,如果有帮助,欢迎 在看
、 点赞
、 收藏
、 分享
给身边的朋友。
真诚推荐你关注我,公众号:ByteCode ,持续分享硬核原创内容,Kotlin、Jetpack、性能优化、系统源码、算法及数据结构、动画、大厂面经。
近期必读热门文章
- 揭秘反射真的很耗时吗,射 10 万次耗时多久
- 你知道 Iterable 有多慢吗?试试它提升性能
- 揭秘 Kotlin 1.6.20 重磅功能 Context Receivers
- Stack Overflow 上最热门的 10 个 Kotlin 问题
- Android 12 已来,你的 App 崩溃了吗?
- Google 宣布废弃 LiveData.observe 方法
- 影响性能的 Kotlin 代码(一)
- 揭秘 Kotlin 中的 == 和 ===
最后推荐长期更新和维护的项目
个人博客,将所有文章进行分类,欢迎前去查看 https://hi-dhl.com
KtKit 小巧而实用,用 Kotlin 语言编写的工具库,欢迎前去查看 KtKit
计划建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目以及相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,欢迎前去查看 AndroidX-Jetpack-Practice
LeetCode / 剑指 offer / 国内外大厂面试题 / 多线程题解,语言 Java 和 kotlin,包含多种解法、解题思路、时间复杂度、空间复杂度分析
边栏推荐
- 为什么不建议你用 MongoDB 这类产品替代时序数据库?
- Node の MongoDB Driver
- Svg optimization by svgo
- OpenGL - Model Loading
- 搞数据库是不是越老越吃香?
- 【愚公系列】2022年7月 Go教学课程 003-IDE的安装和基本使用
- [sourcetree configure SSH and use]
- Project practice | excel export function
- Applet data attribute method
- Community group buying has triggered heated discussion. How does this model work?
猜你喜欢
图神经网络+对比学习,下一步去哪?
[ctfhub] Title cookie:hello guest only admin can get flag. (cookie spoofing, authentication, forgery)
【sourceTree配置SSH及使用】
LeetCode 556. Next bigger element III
mysql安装配置以及创建数据库和表
Applet customization component
TDengine 连接器上线 Google Data Studio 应用商店
Wxml template syntax
基于模板配置的数据可视化平台
E-commerce apps are becoming more and more popular. What are the advantages of being an app?
随机推荐
C language - input array two-dimensional array a from the keyboard, and put 3 in a × 5. The elements in the third column of the matrix are moved to the left to the 0 column, and the element rows in ea
Greendao reported an error in qigsaw, could not init daoconfig
A keepalived high availability accident made me learn it again
从“化学家”到开发者,从甲骨文到TDengine,我人生的两次重要抉择
OpenGL - Lighting
百度交易中台之钱包系统架构浅析
7 月 2 日邀你来TD Hero 线上发布会
Kotlin introductory notes (VI) interface and function visibility modifiers
Resolve the horizontal (vertical) sliding conflict between viewpager and WebView
E-commerce apps are becoming more and more popular. What are the advantages of being an app?
[ManageEngine] how to make good use of the report function of OpManager
[JS sort according to the attributes in the object array]
Nips2021 | new SOTA for node classification beyond graphcl, gnn+ comparative learning
[ctfhub] Title cookie:hello guest only admin can get flag. (cookie spoofing, authentication, forgery)
MySQL does not take effect in sorting string types
How to choose the right chain management software?
uni-app---uni.navigateTo跳转传参使用
Project practice | excel export function
Kotlin introductory notes (VIII) collection and traversal
Svg optimization by svgo