本文主要介紹了Java的記憶體機制的相關知識,具有很好的參考價值,以下跟著小編一起來看下吧
Java把記憶體分為兩種:一種是棧內存,另一種是堆內存。 在函數中定義的一些基本類型的變數和物件的參考變數都是在函數的堆疊記憶體中分配,當在一段程式碼區塊定義一個變數時,Java 就在堆疊中為這個變數分配記憶體空間,當超過變數的作用域後(例如,在函數A中呼叫函數B,在函數B中定義變數a,變數a的作用域只是函數B,在函數B運作以後,變數a會自動被銷毀。指派給它的記憶體會被回收),Java會自動釋放掉為該變數所分配的記憶體空間,該記憶體空間可以立即另做他用。
堆內存用來存放由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中文網其他相關文章!