当前位置:网站首页>卷起来,突破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,包含多种解法、解题思路、时间复杂度、空间复杂度分析
边栏推荐
- [hungry dynamic table]
- Global configuration tabbar
- A detailed explanation of the general process and the latest research trends of map comparative learning (gnn+cl)
- Applet customization component
- idea用debug调试出现com.intellij.rt.debugger.agent.CaptureAgent,导致无法进行调试
- OpenGL - Coordinate Systems
- LeetCode 556. Next bigger element III
- Why does everyone want to do e-commerce? How much do you know about the advantages of online shopping malls?
- 【对象数组a与对象数组b取出id不同元素赋值给新的数组】
- A keepalived high availability accident made me learn it again
猜你喜欢
初识结构体
Viewpager pageradapter notifydatasetchanged invalid problem
Node の MongoDB Driver
Three-level distribution is becoming more and more popular. How should businesses choose the appropriate three-level distribution system?
Svgo v3.9.0+
一次 Keepalived 高可用的事故,让我重学了一遍它
High performance spark_ Transformation performance
Unity skframework framework (XXII), runtime console runtime debugging tool
Develop and implement movie recommendation applet based on wechat cloud
代码语言的魅力
随机推荐
About getfragmentmanager () and getchildfragmentmanager ()
Go 语言使用 MySQL 的常见故障分析和应对方法
顶会论文看图对比学习(GNN+CL)研究趋势
7 月 2 日邀你来TD Hero 线上发布会
22-07-04 Xi'an Shanghao housing project experience summary (01)
百度交易中台之钱包系统架构浅析
Kotlin introductory notes (VIII) collection and traversal
百度智能小程序巡检调度方案演进之路
uni-app---uni. Navigateto jump parameter use
[ctfhub] Title cookie:hello guest only admin can get flag. (cookie spoofing, authentication, forgery)
Unity skframework framework (XXII), runtime console runtime debugging tool
百度APP 基于Pipeline as Code的持续集成实践
Android 隐私沙盒开发者预览版 3: 隐私安全和个性化体验全都要
H.265编码原理入门
解决idea调试过程中liquibase – Waiting for changelog lock….导致数据库死锁问题
Nips2021 | new SOTA for node classification beyond graphcl, gnn+ comparative learning
Kotlin introductory notes (I) kotlin variables and non variables
A detailed explanation of the general process and the latest research trends of map comparative learning (gnn+cl)
使用el-upload封装得组件怎么清空已上传附件
Unity SKFramework框架(二十四)、Avatar Controller 第三人称控制