常量池、棧、堆的比較

轉:https://www.cnblogs.com/Eason-S/p/5658230.html

JAVA中,有六個不同的地方可以存儲數據:

1.寄存器:最快的存儲區,位於不同於其他存儲區的地方——處理器內部。寄存器的數量極其有限,所以寄存器由編譯器根據需求進行分配。你不能直接控制,也不能在程序中感覺到寄存器存在的任何跡象。

2. 棧:存放基本類型的變量數據和對象的引用。位於通用RAM中,但通過它的“堆棧指針”可以從處理器哪裏獲得支持。堆棧指針若向下移動,則分配新的內存;若向上移動,則釋放那些內存。這是一種快速有效的分配存儲方法,僅次於寄存器。創建程序時候,JAVA編譯器必須知道存儲在堆棧內所有數據的確切大小和生命週期,因爲它必須生成 相應的代碼,以便上下移動堆棧指針。這一約束限制了程序的靈活性。

3. 堆:一種通用性的內存池(也存在於RAM中),用於存放所以的JAVA對象。堆不同於堆棧的好處是:編譯器不需要知道要從堆裏分配多少存儲區域,也不必知道存儲的數據在堆裏存活多長時間。因此,在堆裏分配存儲有很大的靈活性。當你需要創建一個對象的時候,只需要new寫一行簡單的代碼,當執行這行代碼時,會自動在堆裏進行存儲分配。當然,爲這種靈活性必須要付出相應的代碼。用堆進行存儲分配比用堆棧進行存儲存儲需要更多的時間。  

4. 靜態域:存放靜態成員(static定義的) 。

5. 常量池:存放字符串常量和基本類型常量(public static final)。 常量值通常直接存放在程序代碼內部,這樣做是安全的,因爲它們永遠不會被改變。

6. 非RAM存儲:硬盤等永久存儲空間。如果數據完全存活於程序之外,那麼它可以不受程序的任何控制,在程序沒有運行時也可以存在。 

這裏我們主要關心棧,堆和常量池,對於棧和常量池中的對象可以共享,對於堆中的對象不可以共享。

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

(1)對於字符串:其對象的引用都是存儲在棧中的,如果是編譯期已經創建好(直接用雙引號定義的)的就存儲在常量池中,如果是運行期(new出來的)才能確定的就存儲在堆中。對於equals相等的字符串,在常量池中永遠只有一份,在堆中有多份。

例如:

複製代碼

1 String s1 = "china";
2 String s2 = "china";
3 String s3 = "china";
4 String ss1 = new String("china");
5 String ss2 = new String("china");
6 String ss3 = new String("china");   

複製代碼

對於通過new產生一個字符串(假設爲”china”)時,會先去常量池中查找是否已經有了”china”對象,如果沒有則在常量池中創建一個此字符串對象,然後堆中再創建一個常量池中此”china”對象的拷貝對象。

這也就是有道面試題:String s = new String(“xyz”);產生幾個對象?答:一個或兩個,如果常量池中原來沒有”xyz”,就是兩個。

 

(2)對於基礎類型的變量和常量:變量和引用存儲在棧中,常量存儲在常量池中。

例如:

複製代碼

1 int i1 = 9;
2 int i2 = 9;
3 int i3 = 9; 
4 public static final int INT1 = 9;
5 public static final int INT2 = 9;
6 public static final int INT3 = 9;

複製代碼

對於成員變量和局部變量:成員變量就是方法外部,類的內部定義的變量;局部變量就是方法或語句塊內部定義的變量。局部變量必須初始化。

形式參數是局部變量,局部變量的數據存在於棧內存中。棧內存中的局部變量隨着方法的消失而消失。

成員變量存儲在堆中的對象裏面,由垃圾回收器負責回收。

下面給出一個實例:

複製代碼

 1 class BirthDate {
 2     private int day;
 3     private int month;
 4     private int year;    
 5     public BirthDate(int d, int m, int y) {
 6         day = d; 
 7         month = m; 
 8         year = y;
 9     }
10     省略get,set方法………
11 }
12 
13 public class Test{
14     public static void main(String args[]){
15         int date = 9;
16         Test test = new Test();      
17         test.change(date); 
18         BirthDate d1= new BirthDate(7,7,1970);       
19     }  
20 
21     public void change(int i){
22      i = 1234;
23     }
24 }

複製代碼

對於以上這段代碼,date爲局部變量,i,d,m,y都是形參爲局部變量,day,month,year爲成員變量。下面分析一下代碼執行時候的變化:

1. main方法開始執行:int date = 9;

date局部變量,基礎類型,引用和值都存在棧中。

2. Test test = new Test();

test爲對象引用,存在棧中,對象(new Test())存在堆中。

3. test.change(date);

i爲局部變量,引用和值存在棧中。當方法change執行完成後,i就會從棧中消失。

4. BirthDate d1= new BirthDate(7,7,1970);  

d1爲對象引用,存在棧中,對象(new BirthDate())存在堆中,其中d,m,y爲局部變量存儲在棧中,且它們的類型爲基礎類型,因此它們的數據也存儲在棧中。day,month,year爲成員變量,它們存儲在堆中(new BirthDate()裏面)。當BirthDate構造方法執行完之後,d,m,y將從棧中消失。

5.main方法執行完之後,date變量,test,d1引用將從棧中消失,new Test(),new BirthDate()將等待垃圾回收。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章