java的對象存儲在哪裏?

1、寄存器
寄存器是速度最快的存儲區域,它位於處理器內部,但它的數量有限,所以要按需分配,不能被人控制。
2、堆棧
通常也叫棧,位於RAM中,堆棧指針向下移動,則分配新的內存;向上移動,則釋放那些內存。這種存儲方式速度僅次於寄存器。常用於存放對象引用與基本數據類型,不存放Java對象。棧內存被要求存放在其中的數據的大小、生命週期必須是已經確定的。
3、堆
通用的內存池,位於RAM中,用於存放所有的Java對象。編譯器不需要知道存儲的數據在堆中存活多長時間;當需要一個對象時,用new寫一行代碼,當執行這行代碼時,會自動在堆中進行存儲分配,同時,有以上原因,用堆進行存儲分配和清理可能比堆棧進行存儲分配花更多的時間。
4、常量存儲
常量值通常直接存放在程序代碼內部,這樣做是安全的,因爲它們永遠不會改變。但有時在嵌入式系統中,常量本身會和其他部分隔開,放在ROM中。
5、非RAM存儲
如果數據完全存活在程序之外,那麼它可以不受程序控制,程序沒有運行時也可以存在。比如流對象與持久化對象。

特殊:基本類型
在Java中,new創建的對象都存放在堆中,但用new創建一個特別小的,簡單的變量,就比較麻煩,所以Java採用了與c一樣的方法,不用new來創建變量,而是創建一個不是引用的變量,這個變量將值存儲在棧中,而且這些變量佔的內存空間是確定的,不隨機器硬件架構而變化。
Java的數組
在C/C++語言中,數組就是個內存塊,超出內存塊訪問數據或在內存初始化前訪問數據是很危險的;
Java會確保數組被初始化,而且不能在它的範圍外訪問,這種範圍檢查,以每個數組上少量的內存開銷和運行時的下標檢查爲代價。
當創建一個Java數組時,世紀上創建了一個引用數組,每個引用都會初始化爲null。
但創建一個用來存放基本數據類型的數組時,Java也會把它初始化,就算將數組所佔的內存全部置爲0。


在Java中,棧裏面的數據是可以共享的
比如我們定義一個變量:

int a = 1;
int b = 1;

編譯器就會先在棧中開闢一塊空間存儲引用a,然後在棧中查看是否有1這個值存在,發現沒有,則在棧中開闢空間存儲1這個值;然後再開闢空間存儲b,在棧中查看是否有1,發現存在1這個值,那就把b指向1的地址。
用這樣數據共享的方式可以節省空間,防止反覆的向內存加入同樣的值。


Java的String是比較特殊的。
比如這行代碼

String s = “123”;
String b = “123”;
System.out.print(s == b);

這段代碼先在字符串常量池中查找是否有”123“這個字符串(關於字符串常量池可以看這裏),沒有的話,則在字符串常量池中創建。很明顯,b字符串創建時常量池裏已經有123這個字符串了,把它指向123的地址就好了,所以打印出true。

String sc = new String(“123”);
String s = “123”;

這兩行代碼的區別是:
第一行代碼的步驟是
1、先定義一個名爲sc的引用
2、然後檢索常量池中是否有123的字符串
3、如果不存在,則在字符串常量池中存儲進一個值爲"abc"的字符串對象。
4、在堆中創建存儲一個”abc“字符串對象
5、將對象引用指向堆中的對象
第二行代碼和第一行代碼的前三步都一樣,但後面則是直接將s指向字符串常量池當中的”123“對象。
這裏指的注意的是,採用new的方式,雖然是在堆中存儲對象,但是也會在存儲之前檢查常量池中是否已經含有此對象,如果沒有,則會先在常量池創建對象,然後在堆中創建這個對象的”拷貝對象“。這也就是爲什麼有道面試題:String s = new String(“xyz”);產生幾個對象?的答案是:一個或兩個的原因。因爲如果常量池中原來沒有”xyz”,就是兩個。


最後再轉載一個網上看到的一個例子,幫助對棧內存、堆內存的存儲進行理解:

class BirthDate {
    private int day;
    private int month;
    private int year;
    public BirthDate(int d, int m, int y) {
        day = d;
        month = m;
        year = y;
    }
    省略get,set方法………
}

public class Test {
    public static void main(String args[]) {
        int date = 9;
        Test test = new Test();
        test.change(date);
        BirthDate d1 = new BirthDate(7, 7, 1970);
    }

    public void change1(int i) {
        i = 1234;
    }
}

對於以上這段代碼,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);
    調用change(int i)方法,i爲局部變量,引用和值存在棧中。當方法change執行完成後,i就會從棧中消失。
  4. BirthDate d1= new BirthDate(7,7,1970);
    調用BIrthDate類的構造函數生成對象。
    d1爲對象引用,存在棧中;
    對象(new BirthDate())存在堆中;
    其中d,m,y爲局部變量存儲在棧中,且它們的類型爲基礎類型,因此它們的數據也存儲在棧中;
    day,month,year爲BirthDate對象的的成員變量,它們存儲在堆中存儲的new BirthDate()對象裏面;
    當BirthDate構造方法執行完之後,d,m,y將從棧中消失。
  5. main方法執行完之後。
    date變量,test,d1引用將從棧中消失;
    new Test(),new BirthDate()將等待垃圾回收器進行回收。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章