当前位置:网站首页>卷起来,突破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,包含多种解法、解题思路、时间复杂度、空间复杂度分析
边栏推荐
- LeetCode 556. 下一个更大元素 III
- The most comprehensive promotion strategy: online and offline promotion methods of E-commerce mall
- SQL learning alter add new field
- [Yugong series] go teaching course 003-ide installation and basic use in July 2022
- SMT32H7系列DMA和DMAMUX的一点理解
- 云计算技术热点
- High performance spark_ Transformation performance
- Hosting environment API
- SQL learning - case when then else
- Lepton 无损压缩原理及性能分析
猜你喜欢
Unity skframework framework (24), avatar controller third person control
The popularity of B2B2C continues to rise. What are the benefits of enterprises doing multi-user mall system?
Nips2021 | new SOTA for node classification beyond graphcl, gnn+ comparative learning
Dry goods sorting! How about the development trend of ERP in the manufacturing industry? It's enough to read this article
Vs code problem: the length of long lines can be configured through "editor.maxtokenizationlinelength"
7 月 2 日邀你来TD Hero 线上发布会
Kotlin introductory notes (V) classes and objects, inheritance, constructors
OpenGL - Model Loading
【数组的中的某个属性的监听】
LeetCode 556. Next bigger element III
随机推荐
解决idea调试过程中liquibase – Waiting for changelog lock….导致数据库死锁问题
基于STM32单片机的测温仪(带人脸检测)
【el-table如何禁用】
Lepton 无损压缩原理及性能分析
Viewpager pageradapter notifydatasetchanged invalid problem
图神经网络+对比学习,下一步去哪?
项目实战 | Excel导出功能
基于宽表的数据建模应用
c语言指针深入理解
[reading notes] Figure comparative learning gnn+cl
Applet customization component
Why does everyone want to do e-commerce? How much do you know about the advantages of online shopping malls?
Progressive JPEG pictures and related
LeetCode 496. Next larger element I
Using request headers to develop multi terminal applications
[Yugong series] go teaching course 003-ide installation and basic use in July 2022
Lepton 无损压缩原理及性能分析
Can't find the activitymainbinding class? The pit I stepped on when I just learned databinding
uni-app---uni. Navigateto jump parameter use
Node の MongoDB Driver