簡述:
相信每個(gè)C程序員都知道銷毀對象的重要性,也熟悉析構(gòu)函數(shù)蚂会,但是java程序員可能就不太會(huì)關(guān)注銷毀對象计济。因?yàn)閖ava有垃圾回收器負(fù)責(zé)回收無用對象占據(jù)的內(nèi)存資源。當(dāng)然也有特殊情況:假定你的對象(并非使用new)獲得了一塊“特殊”的內(nèi)存區(qū)域嵌器,由于垃圾回收器只知道釋放那些由new分配的內(nèi)存,所以它不知道該如何釋放這塊“特殊內(nèi)存”谐丢。
為了應(yīng)對這個(gè)特殊的情況爽航,java允許在類中定義一個(gè)名為finalize()的方法。它的工作原理“假定”是這樣的:一旦垃圾回收器準(zhǔn)備好釋放對象占用的存儲(chǔ)空間庇谆,將首先調(diào)用其finalize()方法岳掐。并且在下一次垃圾回收動(dòng)作發(fā)生時(shí),才會(huì)真正回收對象占用的內(nèi)存饭耳。所以要是你打算用finalize(),就能在垃圾回收時(shí)刻做一些重要的清理工作串述。
這里有一潛在的編程陷阱,因?yàn)橛行┏绦騿T(特別是C++程序員)剛開始可能會(huì)把finalize()當(dāng)做C++中的析構(gòu)函數(shù)寞肖。所有有必要明確區(qū)分一下纲酗。在C++中如果程序沒有缺陷的話,那么對象一定會(huì)被銷毀新蟆。而java的對象卻并非總是被垃圾回收觅赊,或者換句話說:
1.對象可能不被垃圾回收。
2.垃圾回收并不等于“析構(gòu)”琼稻。
因?yàn)槔厥毡旧硪矔?huì)有開銷吮螺,如果不使用它,那么就不會(huì)支付這部分開銷帕翻。所以當(dāng)程序沒有瀕臨存儲(chǔ)空間用完的那一刻鸠补,對象占用的空間就總也得不到釋放。如果程序執(zhí)行結(jié)束嘀掸,并且垃圾回收器一直都沒有釋放你創(chuàng)建的任何對象的存儲(chǔ)空間紫岩,則隨著程序的退出,那些資源也會(huì)全部交還給操作系統(tǒng)睬塌。所以這里的第三點(diǎn):
3.垃圾回收只與內(nèi)存有關(guān)
finalize()的用途何在:
使用垃圾回收器的唯一原因是為了回收程序不再使用的內(nèi)存泉蝌。所以對于與垃圾回收有關(guān)的任何行為來說(尤其是finalize方法),它們也必須同內(nèi)存及其回收有關(guān)揩晴。
但是并不意味之對象中含有其他對象勋陪,finalize()就應(yīng)該明確釋放那些對象。無論對象是如何創(chuàng)建文狱,垃圾回收器都會(huì)負(fù)責(zé)釋放對象占據(jù)的所有內(nèi)存粥鞋。這就將對finalize()的需求先知道一種特殊情況,即通過某種創(chuàng)建對象方式為對象分配了存儲(chǔ)空間瞄崇。而這種方式非java中通常的做法呻粹。這種情況主要發(fā)生在使用“本地方法”的情況下,本地方法是一種在java中調(diào)用非java代碼的方式苏研。本地方法目前只支持C和C++等浊,但他們可以調(diào)用其他語言寫的代碼,所以實(shí)際上可以調(diào)用任何代碼摹蘑。在非java代碼中筹燕,也許會(huì)調(diào)用C的malloc()函數(shù)系列來分配存儲(chǔ)空間,而且出分調(diào)用了free()函數(shù)衅鹿,否則存儲(chǔ)空間將得不到釋放撒踪,從而造成內(nèi)存泄漏。當(dāng)然大渤,free()是C和C++中的函數(shù)制妄,所以需要在finalize()中用本地方法調(diào)用它。所以要有finalize()泵三。
必須要實(shí)施清理
要清理一個(gè)對象耕捞,用戶必須需要清理的時(shí)刻調(diào)用執(zhí)行清理動(dòng)作的方法。這聽起來似乎很簡單烫幕。在C++中創(chuàng)建了一個(gè)局部對象(也就是在堆棧中俺抽,這在java中是行不通的),應(yīng)該被銷毀较曼,但是程序員忘記調(diào)用delete()函數(shù)磷斧,那么永遠(yuǎn)不會(huì)調(diào)用析構(gòu)函數(shù),這樣就會(huì)出現(xiàn)內(nèi)存泄露捷犹,對象的其他部分也不會(huì)得到清理弛饭。這種缺陷很難跟蹤。
相反伏恐,java不允許創(chuàng)建局部對象孩哑, 必須使用new創(chuàng)建對象。在java中翠桦,也沒有用于釋放對象的delete横蜒,因?yàn)槔厥掌鲿?huì)幫助你釋放存儲(chǔ)空間。甚至可以膚淺的認(rèn)為销凑,正是由于垃圾收集機(jī)制的存在丛晌,使得java沒有析構(gòu)函數(shù)。然而斗幼,垃圾回收器的存在并不能完全代替析構(gòu)函數(shù)澎蛛。(而且絕對不能直接調(diào)用finalize(),因?yàn)閒inalize方法是Object類的一個(gè)Protected方法蜕窿。Protected方法只能被子類內(nèi)部調(diào)用谋逻,外部不能直接調(diào)用 呆馁,所以這也不是一種解決方案。)如果希望進(jìn)行除釋放存儲(chǔ)空間之外的清理工作毁兆,還是得明確調(diào)用某個(gè)恰當(dāng)?shù)姆椒ㄕ懵恕_@就等同于使用析構(gòu)函數(shù)了,只是乜有它方便气堕。
無論是“垃圾回收”還是“終結(jié)”纺腊,都不保證一定會(huì)發(fā)生。如果java虛擬機(jī)并未面臨內(nèi)存消耗的情形茎芭,他是不會(huì)浪費(fèi)時(shí)間去執(zhí)行垃圾回收以恢復(fù)內(nèi)存的揖膜。
class Book {
boolean checkedOut = false;
public Book(boolean checkOut) {
// TODO Auto-generated constructor stub
checkedOut = checkOut;
}
void checkIn() {
checkedOut = false;
}
protected void finalize() {
if (checkedOut) {
System.out.println("Error: checked out");
}
//Nomally,you'll also do this梅桩。
//super.finalize();
}
}
public class TestFinalize {
public static void main(String[] args) {
Book novel = new Book(true);
novel.checkIn();
new Book(true);
System.gc();
}
}
本例的終結(jié)條件是:所有Book對象再被當(dāng)做垃圾回收錢都應(yīng)該被簽入(check in)壹粟。但是mian()方法中,由于程序員的錯(cuò)誤摘投,有一本書未被簽入煮寡。如果沒有finalize()來驗(yàn)證終結(jié)條件,將很難發(fā)現(xiàn)這種缺陷犀呼。
注意幸撕,System.gc()用于強(qiáng)制進(jìn)行終結(jié)動(dòng)作。即使不這么做外臂,通過重復(fù)地執(zhí)行程序(假設(shè)程序?qū)⒎峙浯罅康拇鎯?chǔ)空間而導(dǎo)致垃圾回收動(dòng)作的執(zhí)行)坐儿,最終也能找出錯(cuò)誤的Book對象。
你應(yīng)該總是假設(shè)基類版本的finalize()也要做某些重要的事情宋光,因此要使用super來調(diào)用它貌矿,就像在Book.finalize()中看到的那樣。在本例中罪佳,它被注釋掉了逛漫,因?yàn)樗枰M(jìn)行異常處理。