当前位置:网站首页>卷起來,突破35歲焦慮,動畫演示CPU記錄函數調用過程
卷起來,突破35歲焦慮,動畫演示CPU記錄函數調用過程
2022-07-05 09:45: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 指令地址),返回調用處繼續執行。

正常退出時會做以下件事
- 恢複上一個棧幀局部變量錶、操作數棧
- 如果有返回值,將返回值壓入調用者棧幀的操作數棧,是否有返回值根據
returnJVM 指令: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,包含多種解法、解題思路、時間複雜度、空間複雜度分析

边栏推荐
- 一文详解图对比学习(GNN+CL)的一般流程和最新研究趋势
- tongweb设置gzip
- LeetCode 31. Next spread
- Kotlin introductory notes (VII) data class and singleton class
- 解决Navicat激活、注册时候出现No All Pattern Found的问题
- Thermometer based on STM32 single chip microcomputer (with face detection)
- From "chemist" to developer, from Oracle to tdengine, two important choices in my life
- Solve the problem of no all pattern found during Navicat activation and registration
- [team PK competition] the task of this week has been opened | question answering challenge to consolidate the knowledge of commodity details
- NIPS2021 | 超越GraphCL,GNN+对比学习的节点分类新SOTA
猜你喜欢

初识结构体

Lepton 无损压缩原理及性能分析

Vs code problem: the length of long lines can be configured through "editor.maxtokenizationlinelength"

What are the advantages of the live teaching system to improve learning quickly?

SQL learning - case when then else

代码语言的魅力

一文读懂TDengine的窗口查询功能

一文详解图对比学习(GNN+CL)的一般流程和最新研究趋势

正式上架!TDengine 插件入驻 Grafana 官网

Project practice | excel export function
随机推荐
Deep understanding of C language pointer
Nips2021 | new SOTA for node classification beyond graphcl, gnn+ comparative learning
TDengine × Intel edge insight software package accelerates the digital transformation of traditional industries
[two objects merged into one object]
MySQL installation configuration and creation of databases and tables
Kotlin introductory notes (V) classes and objects, inheritance, constructors
STM32 simple multi-level menu (array table lookup method)
TDengine ×英特尔边缘洞见软件包 加速传统行业的数字化转型
C语言-从键盘输入数组二维数组a,将a中3×5矩阵中第3列的元素左移到第0列,第3列以后的每列元素行依次左移,原来左边的各列依次绕到右边
Figure neural network + comparative learning, where to go next?
代码语言的魅力
测试老鸟浅谈unittest和pytest的区别
Tdengine offline upgrade process
[hungry dynamic table]
From "chemist" to developer, from Oracle to tdengine, two important choices in my life
The popularity of B2B2C continues to rise. What are the benefits of enterprises doing multi-user mall system?
Why do offline stores need cashier software?
Are databases more popular as they get older?
LeetCode 496. Next larger element I
Resolve the horizontal (vertical) sliding conflict between viewpager and WebView