轉: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()將等待垃圾回收。