Java把記憶體分成兩種:一種是堆疊內存,另一種是堆疊內存。在函數中定義的一些基本型別的變數和物件的引用變數都是在函數的堆疊記憶體中分配,當在一段程式碼區塊定義一個變數時,Java 就在棧中為這個變數分配記憶體空間,當超過變數的作用域後(比如,在函數A中呼叫函數B ,在函數B中定義變數a,變數a的作用域只是函數B,在函數B運行以後,變數a會自動被銷毀。釋放掉為該變數分配的記憶體空間,該記憶體空間可以立即另做他用。
堆記憶體用來存放由new創建的記憶體陣列,在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。 在堆中產生一個數組或物件之後,還可以在堆疊中定義一個特殊的變量,讓堆疊中的這個變量的取值等於數組或物件在堆疊內存中的首地址,棧中的這個變量就變成了數組或對象的引用變量,以後就可以在程式中使用棧中的變量來訪問堆中的數組或者對象,引用變量就相當於為數組或者對象起的一個名字。 引用變數是普通的變量,定義時在堆疊中分配,引用變數在程式運行到其他作用域之外後邊釋放。而陣列和物件本省在堆中分配,即使程式運行到使用new產生的陣列或物件的語句所在的程式碼區塊之外,陣列和物件本省佔據的記憶體不會被釋放。陣列和物件在沒有引用變數指向它的時候,才變成垃圾,不能再被使用,在隨後的一個不確定時間被垃圾回收器收走(釋放掉)。這也是Java比較佔記憶體的原因,實際上,堆疊中的變數指向堆疊記憶體中的變量,這就是Java中的指標。
程式碼實例Demo1:單一物件建立
class Person { String name ; int age ; public void tell() { System.out.println("姓名:"+name+",年龄:"+age); } } public class Demo1 { public static void main(String[] args) { Person per = new Person() ; } }
在上述程式中實例化了一個物件per,在實例化的過程中需要再記憶體中開啟空間,其中包括堆疊內存和堆內存,具體的內存分配如下圖所示:
圖1-1 物件的實例化過程
我們可以從上圖中發現,物件名稱per被保存在了堆疊記憶體中(更準確的說法是,在堆疊記憶體中保存的是堆記憶體空間的存取位址),而物件的具體內容,例如屬性name和age,被保存在堆記憶體中。因為per物件只是被實例化,還沒有被具體賦值,所以都是預設值。 字串的預設值為null,int的型別的預設值為0。前面已經提到,堆記憶體空間必須使用new關鍵字才能開啟。
程式碼實例Demo2:多個物件建立
class Person { String name ; int age ; public void tell() { System.out.println("姓名:"+name+",年龄:"+age); } } public class Demo2 { public static void main(String[] args) { Person per1 = new Person() ; Person per2 = new Person() ; per1.name="张三" ; per1.age=30 ; per2.age=33 ; per1.tell(); per2.tell(); } }
圖1-2 實例化兩個物件 關鍵概念:類別跟陣列一樣,都是屬於參考類型,引用型別就是指同一個堆記憶體可以被多個棧記憶體指向,下面來看引用傳遞的簡單實例。
程式碼實例Demo3:物件參考傳遞1######
class Person { String name ; int age ; public void tell() { System.out.println("姓名:"+name+",年龄:"+age); } } public class Demo3 { public static void main(String[] args) { Person per1 = new Person() ; Person per2 = per1 ;//-------注意-------- per1.name="张三" ; per1.age=30 ; per2.age=33 ; per1.tell(); per2.tell(); } }###程式運行結果為:##############從程式的運行結果可以發現,###兩個物件輸出的內容一樣,實際上所謂的引用傳遞,就是將一個堆疊記憶體空間的使用權交給多個堆疊記憶體空間,每個棧記憶體空間都可以修改堆記憶體空間的內容###,此程式的記憶體分配圖如下所示:###
图1-3 对象引用的传递内存分配
图1-3 对象引用的传递内存分配(续)
注意:上述实例中对象per2没有堆内存空间,这是因为对象per2只进行声明操作,也没有进行实例化操作。只是使用new关键字,实例化以后才会有堆内存空间
代码实例Demo4:对象引用传递2
class Person { String name ; int age ; public void tell() { System.out.println("姓名:"+name+",年龄:"+age); } } public class Demo4 { public static void main(String[] args) { Person per1 = new Person() ; Person per2 = new Person() ; per1.name="张三" ; per1.age=30 ; per2.name="李四" ; per2.age=33 ; per2=per1 ;//-----注意---- per1.tell(); per2.tell(); } }
上述运行程序结果为:
从程序的输出结果可以发现跟Demo3差不多。不过内存分配发生了一些变化,具体如下所示:
图1-4 (垃圾对象)的产生
注意点:
1.Java本身提供垃圾收集机制(Garbage Collection,GC),会不定期释放不用的内存空间,只要对象不用了,就会等待GC释放空间,如上面堆内存中的name="李四";age=33。
2.一个栈内存只能指向一个堆内存空间,如果要想指向其他堆内存空间,则必须先断开已有的指向,才能分配新的指向。
Java中常见的内存区域
在Java中主要存在4块内存空间,这些内存的名称及作用如下:
1.栈内存空间:保存所有对象的名称。
2.堆内存空间:保存每个对象的具体属性内容。
3.全局数据区:保存static类型的属性值。
4.全局代码区:保存所有的方法定义。
以上是圖文詳解Java的記憶體機制(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!