Java將記憶體分為兩種:一種是棧記憶體,一種是堆記憶體。
1、 棧(stack)與堆(heap)都是Java用來在Ram(隨機存取儲存空間)中存放資料的地方。與C++不同,Java自動管理棧和堆,程式員不能直接地設定棧或堆。
2、棧的優勢是,存取速度比堆要快,僅次於直接位於CPU中的寄存器,棧資料可以共用。但缺點是,存在棧中的資料大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變數(int, short, long, byte, float, double, boolean, char)和物件控點。JVM規範讓每個Java線程擁有自己的獨立的JVM棧,也就是Java方法的調用棧(多個線程共用堆)。在函數中定義的一些基本類型的變數和對象的引用變數都是在函數的棧記憶體中分配。當在一段代碼塊中定義一個變數時,java就在棧中為這個變數分配記憶體空間,當超過變數的範圍後,java會自動釋放掉為該變數分配的記憶體空間,該記憶體空間可以立刻被另作他用。
堆的優勢是可以動態地分配記憶體大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配記憶體的,Java的垃圾收集器會自動收走這些不再使用的資料。但缺點是,由於要在運行時動態分配記憶體,存取速度較慢。
3、儲存在棧中的資料可以共用。
假設定義:int a = 3,b = 3.
① 編譯器先處理a = 3。首先它會在棧中建立一個變數為a的引用,然後在尋找有沒有字面值為3的地址,如果沒有,就開闢一個存放3這個字面值的地址,然後將a指向3的地址。
② 順序下來,接著就是處理b = 3。在建立完b的引用變數後,由於在棧中已經有3這個字面值,便將b直接指向3的地址。這樣就出現了a與b同事均指向3的情況。
③ 特別注意的是,這種字面值的引用於類對象的引用不同,通過字面值的引用來修改值,不會導致另一個指向此字面值的引用的值也跟著變化。如果此時我們讓a = 4,那麼它會重新搜尋棧中是否有4的字面值,如沒有,就重新開闢地址存放4的值;如有,則直接將a指向這個地址。
此外,String是一個特殊的封裝類資料。可以用:
String str = new String("abc");
String str = "abc";
兩種的形式來建立,第一種是用new()來建立對象的,它會在存放於堆中。每調用一次就會建立一個新的對象。
而第二種是先在棧中建立一個對String類的對象引用變數str,然後尋找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,並令str指向”abc”,如果已經有”abc” 則直接令str指向“abc”。
因此:
情況一:
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
情況二:
String str1 =new String("abc");
String str2 =new String("abc");
System.out.println(str1==str2); //false
情況三:
String s1 = "ja";
String s2 = "va";
String s3 = "java";
String s4 = s1 + s2;
System.out.println(s3 == s4);//false
System.out.println(s3.equals(s4));//true
4、引用變數是普通變數,定義時在棧中分配記憶體,引用變數在程式運行到範圍外釋放。而數組&對象本身在堆中分配,即使程式運行到使用new產生數組和對象的語句所在地代碼塊之外,數組和對象本身佔用的堆記憶體也不會被釋放,數組和對象在沒有引用變數指向它的時候,才變成垃圾,不能再被使用,但是仍然佔著記憶體,在隨後的一個不確定的時間被記憶體回收行程釋放掉。這個也是java比較占記憶體的主要原因。