Java把內存分成兩種,一種叫做棧內存,一種叫做堆內存。
在函數中定義的一些基本類型的變量和對象的引用變量都是在函數的棧內存中分配。當在一段代碼塊中定義一個變量時,java就在棧中為這個變量分配內存空間,當超過變量的作用域后,java會自動釋放掉為該變量分配的內存空間,該內存空間可以立刻被另作他用。
堆內存用於存放由new創建的對象和數組。在堆中分配的內存,由java虛擬機自動垃圾回收器來管理。在堆中產生了一個數組或者對象后,還可以在棧中定義一個特殊的變量,這個變量的取值等於數組或者對象在堆內存中的首地址,在棧中的這個特殊的變量就變成了數組或者對象的引用變量,以后就可以在程序中使用棧內存中的引用變量來訪問堆中的數組或者對象,引用變量相當於為數組或者對象起的一個別名,或者代號。
引用變量是普通變量,定義時在棧中分配內存,引用變量在程序運行到作用域外釋放。而數組&對象本身在堆中分配,即使程序運行到使用new產生數組和對象的語句所在地代碼塊之外,數組和對象本身占用的堆內存也不會被釋放,數組和對象在沒有引用變量指向它的時候,才變成垃圾,不能再被使用,但是仍然占着內存,在隨后的一個不確定的時間被垃圾回收器釋放掉。這個也是java比較占內存的主要原因,實際上,棧中的變量指向堆內存中的變量,這就是 Java 中的指針!
示例1如下:
Person per = new Person();
//這其實是包含了兩個步驟,聲明和實例化
Person per = null; //聲明一個名為Person類的對象per
per = new Person(); // 實例化這個per對象
聲明 指的是創建類的對象的過程;
實例化 指的是用關鍵詞new來開辟內存空間。
它們在內存中的划分是這樣的:
那什么是棧內存(heap)和棧內存(heap)呢?
棧內存:
在函數中定義的一些基本類型的變量和對象的引用變量都在函數的棧內存中分配。棧內存主要存放的是基本類型類型的數據 如( int, short, long, byte, float, double, boolean, char) 和對象句柄。注意:並沒有String基本類型、在棧內存的數據的大小及生存周期是必須確定的、其優點是寄存速度快、棧數據可以共享、缺點是數據固定、不夠靈活。
棧的共享:
1 String str1 = "myString"; 2 3 String str2 = "myString"; 4 5 System.out.println(str1 ==str2 ); 6 7 //注意:這里使用的是str1 ==str2,而不是str1.equals(str2)的方式。 8 9 //因為根據JDK的說明,==號只有在兩個引用都指向了同一個對象時才返回真值 10 11 //而str1.equals(str2),只是比較兩個字符串是否相等
結果為True,這就說明了str1和str2其實指向的是同一個值。
上述代碼的原理是,首先在棧中創建一個變量為str1的引用,然后查找棧中是否有myString這個值,如果沒找到,就將myString存放進來,然后將str1指向myString。接着處理String str2 = "myString";;在創建完str2 的引用變量后,因為在棧中已經有myString這個值,便將str2 直接指向myString。這樣,就出現了str1與str2 同時指向myString。
特別注意的是,這種字面值的引用與類對象的引用不同。假定兩個類對象的引用同時指向一個對象,如果一個對象引用變量修改了這個對象的內部狀態,那么另一個對象引用變量也即刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導致另一個指向此字面值的引用的值也跟着改變的情況。如上例,我們定義完str1與str2 的值后,再令str1=yourString;那么,str2不會等於yourString,還是等於myString。在編譯器內部,遇到str1=yourString;時,它就會重新搜索棧中是否有yourString的字面值,如果沒有,重新開辟地址存放yourString的值;如果已經有了,則直接將str1指向這個地址。因此str1值的改變不會影響到str2的值。
堆內存:
堆內存用來存放所有new 創建的對象和 數組的數據
1 String str1 = new String ("myString"); 2 3 String str2 = "myString"; 4 5 System.out.println(str1 ==str2 ); //False 6 7 String str1 = new String ("myString"); 8 9 String str2 = new String ("myString"); 10 11 System.out.println(a==b); //False
創建了兩個引用,創建了兩個對象。兩個引用分別指向不同的兩個對象。以上兩段代碼說明,只要是用new()來新建對象的,都會在堆中創建,而且其字符串是單獨存值的,即使與棧中的數據相同,也不會與棧中的數據共享。
為了深入理解,添加了實例2:
1 public class Demo1 { 2 3 public static void main(String[] args) { 4 5 String str1 = "hello"; 6 String str2 = "hello"; 7 String str3 = new String("hello"); 8 String str4 = new String("hello"); 9 System.out.println("str1==str2?"+(str1==str2)); // true 10 System.out.println("str2==str3?"+(str2==str3)); //false 11 System.out.println("str3==str4?"+(str3==str4)); // false 12 System.out.println("str3.equals(str2)?"+(str3.equals(str4))); //true 13 //是String類重寫了Object的equals方法,比較的是兩個字符串對象 的內容 是否一致。 14 // "=="用於比較 引用數據類型數據的時候比較的是兩個對象 的內存地址,equals方法默認情況下比較也是兩個對象 的內存地址。 15 16 test(null); 17 } 18 19 }
實行原理如下: