2010年9月25日 星期六

Java Stack(堆疊, 棧) and Heap(堆積, 堆)

stack 和 heep 都是內存的一部分
stack 空間小,速度比較快, 用來放對象的引用
heep 大,一般所有創建的對象都放在這里。

堆疊(stack)可以想像成一個一個疊起來的盒子,數值型別的變數就一個一個放在盒子內。當變數生命周期結束時,盒子就會被移走。

堆積(heap)就像一個空地內亂七八糟的擺了一堆盒子,然後盒子上有標明這個盒子目前是屬於誰在使用的(可以很多人使用同一個盒子)。每當new 出一個物件,例如: Customer c = new Customer(); 物件參照 c 就擺放在堆疊中的盒子裡,而new出來的物件Customer()就在堆積中找一個沒人用的空盒子來擺。當c的生命周期結束,也就是Customer()沒人使用時,堆積中的盒子就會被註明為沒人使用,系統會不定時的把沒人使用的盒子清空。

棧(stack): 是一個先進後出的數據結構,通常用於保存方法(函數)中的參數,局部變量.
在java中,所有基本類型和引用類型都在棧中存儲.棧中數據的生存空間一般在當前scopes內(就是由{...}括起來的區域).
堆(heap):是一個可動態申請的內存空間(其記錄空閑內存空間的鏈表由操作系統維護),C中的malloc語句所產生的內存空間就在堆中.
在java中,所有使用new xxx()構造出來的對象都在堆中存儲,當垃圾回收器檢測到某對象未被引用,則自動銷毀該對象.所以,理論上說java中對象的生存空間是沒有限制的,只要有引用類型指向它,則它就可以在任意地方被使用.

差別:
1. 棧(stack)與堆(heap)都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
2. 棧的優勢是,存取速度比堆要快,僅次於直接位於CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。另外,棧數據可以共享,詳見第3點。堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
3. Java中的數據類型有兩種。
一種是基本類型(primitive types), 共有8種,即int, short, long, byte, float, double, boolean, char(註意,並沒有string的基本類型)。這種類型的定義是通過諸如int a = 3; long b = 255L;的形式來定義的,稱為自動變量。值得註意的是,自動變量存的是字面值,不是類的實例,即不是類的引用,這里並沒有類的存在。如int a = 3; 這里的a是一個指向int類型的引用,指向3這個字面值。這些字面值的數據,由於大小可知,生存期可知(這些字面值固定定義在某個程序塊里面,程序塊退出後,字段值就消失了),出於追求速度的原因,就存在於棧中
另外,棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會在棧中創建一個變量為a的引用,然後查找有沒有字面值為3的地址,沒找到,就開辟一個存放3這個字面值的地址,然後將a指向3的地址。接著處理int b = 3;在創建完b的引用變量後,由於在棧中已經有3這個字面值,便將b直接指向3的地址。
這樣,就出現了a與b同時均指向3的情況。特別註意的是,這種字面值的引用與類對象的引用不同。
假定兩個類對象的引用同時指向一個對象,如果一個對象引用變量修改了這個對象的內部狀態,那麽另一個對象引用變量也即刻反映出這個變化。
相反,通過字面值的引用來修改其值,不會導致另一個指向此字面值的引用的值也跟著改變的情況。
如上例,我們定義完a與b的值後,再令a=4;那麽,b不會等於4,還是等於3。在編譯器內部,遇到a=4;時,它就會重新搜索棧中是否有4的字面值,如果沒有,重新開辟地址存放4的值;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。
另一種是包裝類數據,如Integer, String, Double等將相應的基本數據類型包裝起來的類。這些類數據全部存在於堆中,Java用new()語句來顯示地告訴編譯器,在運行時才根據需要動態創建,因此比較靈活,但缺點是要占用更多的時間。
4.String是一個特殊的包裝類數據。即可以用String str = new String("abc");的形式來創建,也可以用String str = "abc";的形式來創建(作為對比,在JDK 5.0之前,你從未見過Integer i = 3;的表達式,因為類與字面值是不能通用的,除了String。而在JDK 5.0中,這種表達式是可以的!因為編譯器在後臺進行Integer i = new Integer(3)的轉換!)。
前者是規範的類的創建過程,即在Java中,一切都是對象,而對象是類的實例,全部通過new()的形式來創建。Java中的有些類,如DateFormat類,可以通過該類的 getInstance()方法來返回一個新創建的類,似乎違反了此原則。其實不然。該類運用了單例模式來返回類的實例,只不過這個實例是在該類內部通過 new()來創建的,而getInstance()向外部隱藏了此細節。那為什麽在String str = "abc";中,並沒有通過new()來創建實例,是不是違反了上述原則?其實沒有。

# public class Test {
# public static void main(String[] args) {
# String str1 = "abc";
# String str2 = "abc";
# String str3 = new String("abc");
# String str4 = new String("abc");
# //str1 str2
# if(str1 == str2) {
# System.out.println("str1 and str2 is stored as stack");
# }
# else {
# System.out.println("str1 and str2 is stored as heap");
# }
# //str3 str4
# if(str3 == str4) {
# System.out.println("str3 and str4 is stored as stack");
# }
# else {
# System.out.println("str3 and str4 stored as heap");
# }
# }
# }



运行结果如下:
str1 and str2 is stored as stack
str3 and str4 stored as heap

產生這種結果的原因:
1.編譯器在處理String str1="abc"時,會首先在棧中創建一個變量"abc",然後用str1指向"abc",然後處理String str2="abc",編譯器查找棧中已經存在了變量"abc",直接將str2指向已經存在的"abc"變量,所以str1==str2執行結果為 true;
2.編譯器在處理String str3=new String("abc")時,在堆中創建一個新的"abc"對象,然後用str3指向這個對象,然後處理String str4=new String("abc"),在堆中再次創建一個新的"abc"對象,然後用str4指向這個對象,所以str3和str4分別為不同對象的引用,執行 str3==str4為false;

沒有留言:

張貼留言