java 中的堆和棧


 

Java中的內存划分

Java程序在運行時,需要在內存中分配空間。為了提高運行效率,就對數據進行了不同的空間划分。因為每一片區域都有特定的數據處理方式和內存管理方式。

具體分為5種內存空間:

  1. 程序計數器:保證線程切換后能恢復到原來的執行位置。
  2. 虛擬機棧:(棧內存)為虛擬機執行java方法服務,方法被調用時,創建棧幀-
  3. 本地方法棧:為虛擬機執行使用到的Native方法服務
  4. 堆內存:存放所有new出來的東西
  5. 方法區:存儲被虛擬機加載的類信息,常量,靜態常量,靜態方法等。
  6. 運行時常量池(方法區的一部分)

 GC對他們的回收:

內存區域中的程序計數器、虛擬機棧、本地方法棧這3個區域隨着線程而生,線程而滅;棧中的棧幀隨着方法的進入和退出而有條件的執行出棧和入棧的操作。每個棧幀中分配多少內存基本是在類結構確定下來時就已知的。在這個區域不需要過多的考慮回收的問題,因為方法結束或者線程結束時,內存自然就跟着回收了。

GC回收的主要對象:Java堆和方法區

一個接口中的多個實現類需要的內存可能不同,一個方法中的多個分支需要的內存也可能不一樣,我們只有在程序處於運行期間時才能知道會創建哪些對象,這部分內存的分配和回收都是動態的,GC關注的也是這部分內存。

 

 

1.程序計數器:(線程私有)

每隔線程擁有一個程序計數器,在線程創建時創建,指向嚇一跳指令的地址

執行本地方法時,其值為undefined。

說的通俗一點,我們知道,Java是支持多線程的,程序先去執行A線程,執行到一半然后去執行B線程,然后又跑去接着執行A線程,那程序是怎么記住A線程已經執行到哪里了呢?這就需要程序計數器了。因此,為了線程切換后能夠恢復到正確執行的位置,每條線程都有一個獨立的程序計數器,這塊屬於“線程私有”的內存。

 

2.Java虛擬機棧(線程私有)

每隔方法被調用的時候會創建一個棧幀,用於存儲局部變量表,操作棧,動態鏈接,方法出口等信息。局部變量表存放的是:編譯器可知的基本數據類型,對象引用類型。

每個方法被調用直到執行完的過程,就對應着一個棧幀在虛擬機中從引入到出棧的過程。

在Java虛擬機中,對這個區域規定了兩種異常情況:

(1)如果線程請求的棧深度太深,超出了虛擬機所允許的深度,就會出現StackOverFlowError(比如無線遞歸,因為每一層棧幀都會占用一定空間,erXss規定了棧的最大空間,超出這個值就會報錯)

(2)虛擬機棧可以動態擴展,如果擴展到無法申請足夠的內存空間,會出現OOM(OutOfMemory)

 

 

 

3.本地方法棧:

(1)本地方法棧與java虛擬機棧做用戶非常相似,區別是:java虛擬機棧是為虛擬機執行java方法服務的而本地方法棧則是為虛擬機使用到Native方法服務。

(2)Java虛擬機沒有對本地方法棧的使用和數據結構做強制規定,Sun HotSpot虛擬機就把java虛擬機棧和本地方法棧合二為一。

(3)本地方法棧也會拋出StackOverFlowError和OutOfMemoryError

 

 

4.Java堆:堆內存

(1)堆是Java虛擬機所管理的內存區域中罪的的一塊,java堆是被所有縣城共享的內存區域,在java虛擬機啟動時創建堆內存的唯一目的就是存放對象實例,幾乎所有的對象實例都在堆內存分配。

(2)堆是GC管理的主要區域,從垃圾回收的角度看,由於現在的垃圾收集器都是采用的粉黛手機算法,因此java堆還可以初步細分為新生代和老年代。

(3)Java虛擬機規定,堆可以處於物理上不連續的內存空間中,只要邏輯上連續的即可。在實現的既可以是固定的,也可以是動態擴展的。如果在堆內存沒有完成實力分配,並且對大小也無法擴展,就會拋出OutOfMemoryError異常。

 

 

5.方法區(線程共享)

(1)用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼數據。

(2)Sun HotSpot虛擬機把方法區叫做永久帶(permanent Generation),方法區中最終要的部分是運行時常量池。

 

 

6.運行時常量池:

(1)運行時常量池是方法區的一部分,自然受到方法區內存的限制,當常量池無法再申請到內存時就會拋出OutOfMemoryError異常。

 

 

 

一、先說一下最基本的要點

基本數據類型、局部變量都是存放在棧內存中的,用完就消失。
new創建的實例化對象及數組,是存放在堆內存中的,用完之后靠垃圾回收機制不定期自動消除。

 

 

 

二、先明確以上兩點,以下示例就比較好理解了


示例1

main()
  int x=1;
show ()
  int x=2

主函數main()中定義變量int x=1,show()函數中定義變量int x=1。最后show()函數執行完畢。


以上程序執行步驟:

第1步——main()函數是程序入口,JVM先執行,在棧內存中開辟一個空間,存放int類型變量x,同時附值1。
第2步——JVM執行show()函數,在棧內存中又開辟一個新的空間,存放int類型變量x,同時附值2。
     此時main空間與show空間並存,同時運行,互不影響。
第3步——show()執行完畢,變量x立即釋放,空間消失。但是main()函數空間仍存在,main中的變量x仍然存在,不受影響。

示意圖如下:

 

——————————————————————————————————————————————————————————————————————

示例2

main()
  int[] x=new int[3];
  x[0]=20

主函數main()中定義數組x,元素類型int,元素個數3。

以上程序執行步驟
第1步——執行int[] x=new int[3];
  隱藏以下幾分支
  JVM執行main()函數,在棧內存中開辟一個空間,存放x變量(x變量是局部變量)。
  同時,在堆內存中也開辟一個空間,存放new int[3]數組,堆內存會自動內存首地址值,如0x0045。
  數組在棧內存中的地址值,會附給x,這樣x也有地址值。所以,x就指向(引用)了這個數組。此時,所有元素均未附值,但都有默認初始化值0。

第2步——執行x[0]=20
  即在堆內存中將20附給[0]這個數組元素。這樣,數組的三個元素值分別為20,0,0

示圖如下:

——————————————————————————————————————————————————————————————————————

 


免責聲明!

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



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