JVM_03 运行时数据区 [ 虚拟机栈 ]
一、前言
(棧并不是越大越好,越多可以防止出現StackOverflowError晚點出現,但是棧越大,也就代表著虛擬機棧是一定的,你的棧越大,別的棧就會小)
二、 什么是 Java virtual machine?
每創建一個線程就會創建一個Java棧,每一個Java棧中都會有很多棧幀(局部變量表 | 操作數棧 | 動態鏈接 | 方法返回地址 | 一些附加信息) 掌握
解釋:
- (1). 虛擬機棧(Java Virtual Machine Stacks)和線程是緊密聯系的,每創建一個線程時就會對應創建一個Java棧,所以Java棧也是"線程私有"的內存區域,這個棧中又會對應包含多個棧幀,每調用一個方法時就會往棧中創建并壓入一個棧幀,棧幀是用來存儲方法數據和部分過程結果的數據結構,每一個方法從調用到最終返回結果的過程,就對應一個棧幀從入棧到出棧的過程
[先進后出] - (2). 棧幀中有如下部分組成:
- (3). 棧的其他介紹
(8種基本類型的變量+對象的引用變量+實例方法都是在函數的棧內存中分配[局部變量])
- 棧幀過多導致棧內存溢出(方法的遞歸調用,沒設置正確停止條件)
- 棧幀過大(棧幀大小>棧內存)
二、 局部變量表(LocalVariables) 掌握
定義為一個數字數組,主要用于存儲方法參數和定義在方法體內的局部變量(這些數據類型包括各種基本數據類型、對象引用(reference)以及return Address類型)
由于局部變量是建立在線程的棧上,是線程私有數據,因此不存在數據安全問題
局部變量表所需容量大小是在編譯期確定下來的。(并保存在方法Code屬性的maximum local variables數據項中,在方法運行期間不會改變局部變量表的大小的)
jclasslib說明:
- 局部變量表,是基本的存儲單元是slot(變量槽)
- 在局部變量表中,32位以內的類型只占有一個slot(包括引用數據類型),64位的類型(long和double)占有兩個slotbyte、short、char在存儲前被轉換為int,boolean也被轉換為int(0表示fasle,非0表示true) long和double則占據兩個slot
Jvm會為局部變量表中的每一個slot都分配一個訪問索引,通過這個索引即可成功訪問到局部變量表中指定的局部變量值
如果需要訪問局部變量表中一個64bit的局部變量值時,只需要使用前一個索引即可(比如:訪問long或double類型變量)
如果當前幀是由構造方法或者實例方法創建,那么該對象引用this將會放在index為0的slot處
三、操作數棧(operand stack) (掌握)
每一個獨立的棧幀中除了包含局部變量表以外,還包含了一個后進先出的操作數棧,也可以稱之為表達式棧
操作數棧,在方法執行過程中,根據字節碼指令,往棧中寫入數據或提取數據,即入棧或出棧
如果被調用的方法帶有返回值的話,其返回值將會被壓入當前棧幀的操作數棧中
操作數棧,主要用于保存計算機過程的中間結果,同時作為計算過程中變量臨時的存儲空間
操作數棧的具體說明:
解釋:
四 、動態鏈接(Dynamic Linking)
運行時常量池位于方法區,字節碼中的常量池結構如下:
為什么需要常量池呢?
(常量池的作用,就是為了提供一些符號和常量,便于指令的識別。下面提供一張測試類的運行時字節碼文件格式)
每一個棧幀內部都包含一個指向運行時常量池Constant pool或該棧幀所屬方法的引用。包含這個引用的目的就是為了支持當前方法的代碼能夠實現動態鏈接。比如invokedynamic指令
在Java源文件被編譯成字節碼文件中時,所有的變量和方法引用都作為符號引用(symbolic Refenrence)保存在class字節碼文件(javap反編譯查看)的常量池里。比如:描述一個方法調用了另外的其他方法時,就是通過常量池中指向方法的符號引用來表示的,那么動態鏈接的作用就是為了將這些符號引用(#)最終轉換為調用方法的直接引用。
五、 方法的調用:(小插曲)難點
靜態鏈接(早期綁定):當一個 字節碼文件被裝載進JVM內部時,如果被調用的目標方法在編譯期可知,且運行期保持不變時。這種情況下將調用方法的符號引用轉換為直接引用的過程稱之為靜態鏈接
(invokestatic | invokespecial)
動態鏈接(晚期綁定):如果被調用的方法在編譯期無法被確定下來,也就是說,只能夠在程序運行期將調用方法的符號引用轉換為直接引用,由于這種引用轉換過程具備動態性,因此也就被稱之為動態鏈接。體現了多態
(invokevirtual | invokeinterface)
非虛方法: 如果方法在編譯器就確定了具體的調用版本,這個版本在運行時是不可變的。這樣的方法稱為非虛方法
(靜態方法、私有方法、final方法、實例構造器(實例已經確定,this()表示本類的構造器)、父類方法(super調用)都是非虛方法)
其他所有體現多態特性的方法稱為虛方法
如下指令要重點掌握
普通調用指令:
1.invokestatic:調用靜態方法,解析階段確定唯一方法版本;
2.invokespecial:調用方法、私有及父類方法,解析階段確定唯一方法版本;
3.invokevirtual:調用所有虛方法;
4.invokeinterface:調用接口方法; 動態調用指令(Java7新增):
5.invokedynamic:動態解析出需要調用的方法,然后執行 . 前四條指令固化在虛擬機內部,方法的調用執行不可人為干預,而invokedynamic指令則支持由 用戶確定方法版本。
6.其中invokestatic指令和invokespecial指令調用的方法稱為非虛方法
7.其中invokevirtual(final修飾的除外,JVM會把final方法調用也歸為invokevirtual指 令,但要注意final方法調用不是虛方法)、invokeinterface指令調用的方法稱稱為虛方法。
六、 關于invokedynamic指令
JVM字節碼指令集一直比較穩定,一直到java7才增加了一個invokedynamic指令,這是Java為了實現【動態類型語言】支持而做的一種改進
動態類型語言和靜態類型語言兩者的卻別就在于對類型的檢查是在編譯期還是在運行期,滿足前者就是靜態類型語言,反之則是動態類型語言。
Java是靜態類型語言(盡管lambda表達式為其增加了動態特性),js,python是動態類型語言
七、方法返回地址(Return Address)
理解如下話:
(pc寄存器每執行一條指令都會被改變 而返回地址在調用call之前一直是上一條call后面的地址,不改變)
總結
以上是生活随笔為你收集整理的JVM_03 运行时数据区 [ 虚拟机栈 ]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年中国现制茶饮行业洞察报告
- 下一篇: 2021中国餐饮行业数字化调研报告