java中的物件與垃圾回收
java的垃圾回收時java語言得重要功能之一。當程式建立物件、陣列等引用型別實體時,系統都會在堆記憶體中為之分配一塊記憶體區,物件就儲存在這塊記憶體區,當這塊記憶體區不在被任何引用變數所引用時,這塊記憶體就變成了垃圾,等待垃圾回收機制進行回收。
- 垃圾回收機制只負責回收堆記憶體中的物件,不回收任何物理資源;
- 程式無法精確控制垃圾回收執行,垃圾回收會在合適的時候進行。當物件永久性地失去引用後,系統會在合適的時候回收它所佔的記憶體。
- 在垃圾回收機制回收任何物件之前,總先會呼叫它的finalize()方法,該方法可能使該物件重新復活,從而呆滯垃圾回收機制取消。
物件在記憶體中的狀態
package org.westos.Mydemo;
import java.lang.String
public class StatusTranfer {
public static void test() {
String a = new String("我愛java");//1
a=new String ("java我愛你");//2
}
public static void main(String[] args) {
test();
}
}
定義了一個a變數並讓它指向"我愛java"物件,該段程式碼執行完畢後,"我愛java"變成可達狀態,當執行第2段程式碼後,a又指向"java我愛你"物件,第一個物件變成可恢復狀態,第二個物件變成可達狀態。
一個物件可以被一個方法的區域性變數引用,也可以被其他類的類變數引用,或被其他物件的例項變數引用。當某個物件被其他類的類變數引用時,只有當該類被銷燬後,該物件才會進入可恢復狀態;當某個物件被其他物件的實力變數引用時,只有當該物件被銷燬後,該物件會進入可恢復狀態。
強制垃圾回收
當一個物件失去引用後,系統何時呼叫finalize方法對它進行資源管理,何時它會變成不可達物件,系統何時回收它所佔有的記憶體,對於程式完全透明。程式只能控制一個物件何時不再被任何引用變數引用,決不能控制它何時被回收。
雖然系統不能控制時機,但依然可以強制系統進行垃圾回收,其實這種機制只是通知系統進行垃圾回收,但系統是否進行回收依然不確定。
兩種回收方式:
- 呼叫System類的ge靜態方法:System.gc();
- 呼叫Runtime物件的ge例項方法:Runtime.getRuntime().gc();
package org.westos.Mydemo;
public class gcdemo {
public static void main(String[] args) {
for (int i = 0; i <4 ; i++) {
new gcdemo();
// System.gc();
Runtime.getRuntime().gc();
}
}
public void finalize(){
System.out.println("系統正在清理gcdemo物件的資源...");
}
}
這種方式只是程式建議系統回收垃圾,系統完全有可能不會立即回收,垃圾回收機制會在收到通知後儘快進行處理。
finalize方法
java預設使用finalize方法來清理資源。
方法原型為:protected void finalize() throws Throwable
當這個方法執行後,物件消失,垃圾回收機制開始執行。throws Throwable表示它可以丟擲任何型別的異常。
如果程式在終止之前始終沒有進行垃圾回收,則不會呼叫失去引用物件的finalize方法來清理資源。只有當程式認為需要更多的額外記憶體時,垃圾回收機制才會進行垃圾回收。
finalize方法的特點:
- 永遠不要主動呼叫某個物件的finalize方法,該方法應該交給垃圾回收機制呼叫;
- finalize方法的呼叫具有不確定性;
- 當JVM執行可恢復物件的finalize方法時,可能使該物件或系統中的其他物件重新變成可達狀態;
- 當JVM執行finalize方法時出現異常時,垃圾回收機制不會報告異常;
演示如何在finalize方法裡復活自身:
package org.westos.Mydemo;
public class FinalizrTest {
private static FinalizrTest ft=null;
public void info(){
System.out.println("測試資源管理的finalize方法");
}
public static void main(String[] args) {
//建立FinalizrTest物件立即進入可恢復狀態、
new FinalizrTest();
//通知系統進行資源回收
System.gc();//1
//垃圾回收機制呼叫可恢復物件的finalize方法
// Runtime.getRuntime().runFinalization();//2
System.runFinalization();//3
ft.info();
}
public void finalize(){
//讓ft引用到試圖回收可恢復物件
ft=this;
}
}
上面程式碼重寫了finalize方法,讓物件在方法中復活;如果呼叫了2、3程式碼可以看到下列執行結果,也就是說finalize方法立即被執行了,物件隨之復活正常呼叫info方法;
如果將2,3程式碼註釋掉,會發現有時候能呼叫到info方法,有時候會出現下列的空指標異常現象,這就是垃圾回收機制並沒有立即回收物件的結果,也就是沒有及時呼叫finalize方法
物件的軟、弱和虛引用
對大部分物件而言,程式裡會有一個引用變數引用該物件,這是最常見的方式。
java中有四種引用方式:
- 強引用(StrongReference):這種方式是最常見的,程式建立一個物件,並把這個物件賦給一個引用變數,程式通過該引用變數來操作實際的物件。
- 軟引用(SoftReference):需要通過SoftReference類來實現,當一個物件只有軟引用時,他有可能被垃圾回收機制回收,對於只有軟引用的物件而言,當系統記憶體空間足夠時,他不會被回收,程式也可使用該物件,當空間不足時,系統可能會回收它;
- 弱引用(WeakReference):通過WeakReference類實現,弱引用和軟引用很像,但是它的級別更低。當系統垃圾回收機制執行時,不管系統記憶體是否足夠,總會被回收。
- 虛引用(PhantomReference):通過PhantomReference類來實現,類似於沒有引用,主要用於跟蹤物件被垃圾回收的狀態,不能單獨使用,必須和引用佇列聯合使用。
引用佇列由java.lang.ref.ReferenceQueue
類表示,它用於儲存被回收後物件的引用。
package org.westos.demo3;
import java.lang.String;
import java.lang.ref.WeakReference;
public class ReferenceTest {
public static void main(String[] args) {
//建立一個字元物件
String str = new String("我愛java");
//建立一個弱引用並且引用到字串
WeakReference wr = new WeakReference(str);
//切斷引用
str=null;
//取出弱引用所引用的物件,雖然已經切斷了引用,但是記憶體空間不緊張,索引物件未被回收
System.out.println(wr.get());//我愛java
//強制垃圾回收
System.gc();
System.runFinalization();
//再次取出弱引用所引用的物件,由於已經物件已經被回收,所以輸出null
System.out.println(wr.get());//null
}
}
用途
- 可以避免在程式執行期間將物件留在記憶體中;
- 如果以軟引用、弱引用、虛引用來引用物件,垃圾回收器就能夠碎一地釋放物件,如果希望儘可能減小程式在其宣告週期中所佔用的記憶體大小時,這些引用類就很有用。