java對象創建與內存模型總結
1、JVM管轄的內存大致分為三個邏輯部分:java棧(Heap)、java堆(JavaStack)和方法區(MethodArea)。在JVM啟動時創建,關閉時全部回收。
棧、本地方法棧、程序計數器:以線程為粒度,每個線程擁有自己的部分。而堆和方法區被所有線程共享。
堆:運行時的數據區域,程序(線程)運行時動態分配。
方法區:靜態存儲區,存儲被裝載到JVM中的class信息。
2、棧
i)、存放基本類型的變量數據和對象的引用變量(變量本身存放在堆和常量池中)。
ii)、java系統必須知道存儲在棧內所有項的確切生命周期(故變量的垃圾回收由系統處理),以便上下移動棧指針,相比堆更高效,但限制了程序的靈活性。
iii)、可以數據共享(共享的是值,改變一個變量不會影響另一個變量)
int c = 1;int d =1;//c,d共享棧數據1
d = 2;//如果棧中沒有2這個值(如果有就直接引用共享),新建值並將d重新指向它。
3、堆
i)、存放由new操作符創建的對象和數組(‘可以但不必須’將其引用存放在棧中,方便快速訪問)。
ii)、運行時動態分配內存(速度慢於棧的主要原因),並由gc管理。
iii)、堆中的垃圾回收由gc處理(用戶不能幹預,即便調用System.gc()也只是‘希望執行’垃圾回收。因為gc是為所有java應用進程服務且gc的執行會影響所有java應用進程,不特殊對待某個進程)。
4、方法區
i)、位於堆上的靜態存儲區,存儲被裝載到JVM中的class信息。
ii)、常量池: 每個常量池從方法區分配,存放類、方法、接口等中的常量和字符串常量(常量:public static final)。
每個常量池在編譯期確定下來,並保存到.class文件中。
可以數據共享。
5、特殊的String對象
String a1 = "123"; //這一句創建了1個字符串常量對象(常量池中)。
String a2 = "123"; //這兩句還是1個對象。a2也指向a1的字符串常量對象“123”。
String b1 = new String("123"); //這一句本身應該創建兩個對象(先創建常量對象“123”,再用它做參數用new在堆中創建一個新對象)。但因為字符串常量對象“123”已存在(常量池),所以此時只創建1個對象(堆中)
String b2 = new String("123"); //此時因為“123”已存在常量池中,故只用new操作符新建一個對象。
System.out.println(a1 == a2); //運算符 == 比較引用地址
System.out.println(b1 == b2);
對於 "123" --在常量池中創建字符串常量對象“123”(如果常量池中已有該對象就直接引用而不再創建)。共一個對象,位於常量池中。
對於 new String("123") --先在常量池中創建字符串常量對象“123”,再在堆中用new操作符創建新對象(new操作符每次都新建不同內存地址的對象)。共兩個對象,分別存儲於常量池、堆中。
6、垃圾回收
i)、處理位於java邏輯堆上的垃圾對象。
ii)、采用基於“標記-清除”、“停止-復制”算法的“自適應”的垃圾回收技術。進行垃圾回收時會暫停程序的運行。
iii)、當對象失去所有引用成為垃圾後,可能不被垃圾回收。
iiii)、無論“垃圾回收”還是“終結”,都不保證能發生,如果JVM並未面臨內存耗盡的情形,它是不會浪費時間去執行垃圾回收以回復內存。
iiiii)、finalize()正確用法是,在對象即將被回收時,用來做一些善後處理。
iiiiii)、System.gc()用於強制進行垃圾回收動作。但並不能保證垃圾回收動作的執行,頂多增加垃圾回收動作發生的可能性。垃圾回收動作不受用戶幹預,因為gc服務於JVM中的所有進程,不為某個進程特殊處理。
int[] ary = {1,2,3}; //當ary超過作用域後,變量ary將立即在棧中被系統回收並可另作他用。它所指向的數組對象被gc‘標記-清除’,但對象依然存在堆中,等到一個(不確定)時間被gc回收。
6、對象創建的總結
java棧用來存放基本類型的變量數據和對象的引用變量,但不存放對象內容;
java堆存放使用new關鍵字創建的對象和數組對象;
特殊情況是字符串這個包裝類:其引用是存放在棧裏的,而對象內容位置由創建方式來決定(常量池、堆),用“”在編譯期創建的存放在字符串常量池中,用new操作符在運行時創建的存放在堆中。
java對象創建與內存模型總結