Java之道丨Java虛擬機堆棧

關於堆棧,必須知道的是,JVM中,堆是堆(heap),棧是棧(stack),堆棧也是指棧。  

棧(stack),位於通用RAM中,但通過它的“堆棧指針”可以從處理器哪裡獲得支持。堆棧指針若向下移動,則分配新的內存;若向上移動,則釋放那些內存。存儲讀取速度,僅次於寄存器。存放基本類型數據變量和對象、數組等引用類型的引用!

棧幀存儲了方法的局部變量表、操作數棧、動態連接和方法返回地址等信息。

每一個方法從調用開始到執行完成的過程,就對應著一個棧幀在虛擬機棧裡面從入棧到出棧(也叫彈棧)的過程。

Java之道丨Java虛擬機堆棧

對於棧,只有棧頂的棧幀是有效的,稱為當前棧幀,這個棧幀所關聯的方法稱為當前方法。執行引擎所運行的所有字節碼指令都只針對當前棧幀進行操作。

堆(heap),是一種通用性的內存池(也存在於RAM中),用於存放所有的JAVA對象!

Java之道丨Java虛擬機堆棧

幾種存儲空間

各存儲空間就速度來說,有如下關係:寄存器 >堆棧 > 堆 > 其它。

棧中的數據大小和生命週期是可以確定的,當沒有引用指向數據時,這個數據就會消失。堆中的對象的由垃圾回收器gc負責回收。

結合實例:Student student=new Student();JVM在內存中偷偷的做了什麼?

  1. 加載Student.class文件進入內存

  2. 在棧內存中為student開闢空間,棧內存中的局部變量表中存放student變量

  3. 在堆內存中為學生對象開闢空間

  4. 學生對象的成員變量默認初始化(無參構造方法)

  5. 學生對象的成員變量顯示初始化(有參構造方法)(若沒有就不執行)

  6. 通過構造代碼塊對學生對象進行初始化(若沒有就不執行)

  7. 通過構造方法對學生對象進行初始化(通過構造方法對學生對象的成員變量賦值)

    對象初始化完畢

  8. 學生對象初始化完畢後,向外拋出產生的對象地址

  9. 讓變量student指向學生對象

    Java之道丨Java虛擬機堆棧

1.程序計數器

程序計數器(Program Counter Register) 是一塊較小的內存空間,它可以看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裡,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條執行字節碼指令。

每條線程都有一個獨立的程序計數器。

如果執行的是java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令地址。如果是native方法,計數器為空。此內存區域是唯一一個在java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域。

2.Java虛擬機棧

同樣是線程私有,描述Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀(Stack Frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。一個方法對應一個棧幀。

局部變量表存放了各種基本類型、對象引用和returnAddress類型(指向了一條字節碼指令地址)。其中64位長度long 和 double佔兩個局部變量空間,其他只佔一個。

規定的異常情況有兩種:1.線程請求的棧的深度大於虛擬機所允許的深度,將拋出StackOverflowError異常;2.如果虛擬機可以動態擴展,如果擴展時無法申請到足夠的內存,就拋出OutOfMemoryError異常。

3.本地方法棧

和Java虛擬機棧很類似,不同的是本地方法棧為Native方法服務。

4.Java堆

是Java虛擬機所管理的內存中最大的一塊。由所有線程共享,在虛擬機啟動時創建。堆區唯一目的就是存放對象實例。

堆中可細分為新生代和老年代,再細分可分為Eden空間、From Survivor空間、To Survivor空間。

堆無法擴展時,拋出OutOfMemoryError異常

5.方法區

所有線程共享,存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

當方法區無法滿足內存分配需求時,拋出OutOfMemoryError

6.運行時常量池

它是方法區的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項是常量池(Const Pool Table),用於存放編譯期生成的各種字面量和符號引用。並非預置入Class文件中常量池的內容才進入方法運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發人員利用得比較多的便是String類的intern()方法。

當方法區無法滿足內存分配需求時,拋出OutOfMemoryError

7.直接內存

並不是虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域。

JDK1.4加入了NIO,引入一種基於通道與緩衝區的I/O方式,它可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。因為避免了在Java堆和Native堆中來回複製數據,提高了性能。

當各個內存區域總和大於物理內存限制,拋出OutOfMemoryError異常。


分享到:


相關文章: