JVM(二) 棧內存結構


棧內存是描述java方法執行的內存模型,每個方法在執行的同時都會創建一個棧幀(Stack Frame)用於存儲局部變量表、操作數棧、動態鏈接、返回出口等信息。每一個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。棧幀(Frame)是用來存儲數據和部分過程結果的數據結構,同時也被用來處理動態鏈接(Dynamic Linking)、方法返回值和異常分派(Dispatch Exception)。棧幀隨着方法調用而創建,隨着方法結束而銷毀——無論方法是正常完成還是異常完成(拋出了在方法內未被捕獲的異常)都算作方法結束。

1. JVM棧內存結構

1.1 局部變量表

局部變量表(Local Variables Table)也可以稱之為本地變量表,它包含在一個獨立的棧幀中。顧名思義,局部變量表主要用於存儲方法參數和定義在方法體內的局部變量,這些數據類型包括各類原始數據類型、對象引用(reference),以及returnAddress類型。局部變量表所需的容量大小在編譯期就可以被完全確定下來,並保存在方法的Code屬性中。大家思考一下,既然方法體內定義的局部變量是存儲在棧幀中的局部變量表里的,那么原始數據類型的成員變量的值是否也存儲在局部變量表中呢?其實如果是定義在方法體外的成員變量,不止是作用域發生了變化,更重要的是,其值也並非還是存儲在局部變量表里,而是存儲在對象內存空間的實例數據中,整體來看即存儲在Java堆區內。簡單來說,與線程上下文相關的數據存儲在Java棧中,反之則存儲在Java堆區內。

1.2. 操作數棧

每一個獨立的棧幀中除了包含局部變量表以外,還包含一個后進先出(Last-In-First-Out)的操作數棧,也可以稱之為表達式棧(Expression Stack)。操作數棧和局部變量表在訪問方式上存在着較大差異,操作數棧並非采用訪問索引的方式來進行數據訪問的,而是通過標准的入棧和出棧操作來完成一次數據訪問。每一個操作數棧都會擁有一個明確的棧深度用於存儲數值,一個32bit的數值可以用一個單位的棧深度來存儲,而2個單位的棧深度則可以保存一個64bit的數值,當然操作數棧所需的容量大小在編譯期就可以被完全確定下來,並保存在方法的Code屬性中。

1.3. 動態鏈接

每一個棧幀內部除了包含局部變量表和操作數棧之外,還包含一個指向運行時常量池中該棧幀所屬方法的引用,包含這個引用的目的就是為了支持當前方法的代碼能夠實現動態鏈接(Dynamic Linking)。在運行時常量池,一個有效的字節碼文件中除了包含類的版本信息、字段、方法以及接口等描述信息外,還包含一項信息,那就是常量池表(Constant Pool Table),那么運行時常量池就是字節碼文件中常量池表的運行時表示形式。在一個字節碼文件中,描述一個方法調用了另外的其他方法時,就是通過常量池中指向方法的符號引用(Symbolic Reference)來表示的,那么動態鏈接的作用就是為了將這些符號引用轉換為調用方法的直接引用。

1.4. 方法返回值

一個方法在執行的過程中將會產生兩種調用結果:一種是方法正常調用完成,而另外一種則是方法異常調用完成。如果是方法正常調用完成,那么這就意味着,被調用的當前方法在執行的過程中將不會有任何的異常被拋出,並且方法在執行的過程中一旦遇見字節碼返回指令時,將會把方法的返回值返回給它的調用者,不過一個方法在正常調用完成之后究竟需要使用哪一個返回指令還需要根據方法返回值的實際數據類型而定。在字節碼指令中,返回指令包含ireturn(當返回值是boolean、byte、char、short和int類型時使用)、lreturn、freturn、dreturn以及areturn,另外還有一個return指令供聲明為void的方法、實例初始化方法、類和接口的初始化方法使用。

2. 棧上分配

jvm里面new Object對象除了在堆內存創建外,也可以在棧上創建,我們類里面的方法對應一個個的棧幀,方法里面new出來的對象,是局部不共享的。當然我們棧幀里面創建的對象如果太大,也會直接在堆空間分配。

3. 內存逃逸

當我們棧幀里面new出來的對象,如果被外部引用后,此時該對現象不會被分配在棧幀內而是直接分配在堆區間


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM