Java清理:finalize

簡述:

相信每個C程序員都知道銷毀對象的重要性,也熟悉析構(gòu)函數(shù),但是java程序員可能就不太會關(guān)注銷毀對象。因為java有垃圾回收器負(fù)責(zé)回收無用對象占據(jù)的內(nèi)存資源。當(dāng)然也有特殊情況:假定你的對象(并非使用new)獲得了一塊“特殊”的內(nèi)存區(qū)域,由于垃圾回收器只知道釋放那些由new分配的內(nèi)存,所以它不知道該如何釋放這塊“特殊內(nèi)存”。

為了應(yīng)對這個特殊的情況,java允許在類中定義一個名為finalize()的方法。它的工作原理“假定”是這樣的:一旦垃圾回收器準(zhǔn)備好釋放對象占用的存儲空間,將首先調(diào)用其finalize()方法。并且在下一次垃圾回收動作發(fā)生時,才會真正回收對象占用的內(nèi)存。所以要是你打算用finalize(),就能在垃圾回收時刻做一些重要的清理工作。

這里有一潛在的編程陷阱,因為有些程序員(特別是C++程序員)剛開始可能會把finalize()當(dāng)做C++中的析構(gòu)函數(shù)。所有有必要明確區(qū)分一下。在C++中如果程序沒有缺陷的話,那么對象一定會被銷毀。而java的對象卻并非總是被垃圾回收,或者換句話說:

1.對象可能不被垃圾回收。
2.垃圾回收并不等于“析構(gòu)”。

因為垃圾回收本身也會有開銷,如果不使用它,那么就不會支付這部分開銷。所以當(dāng)程序沒有瀕臨存儲空間用完的那一刻,對象占用的空間就總也得不到釋放。如果程序執(zhí)行結(jié)束,并且垃圾回收器一直都沒有釋放你創(chuàng)建的任何對象的存儲空間,則隨著程序的退出,那些資源也會全部交還給操作系統(tǒng)。所以這里的第三點:

3.垃圾回收只與內(nèi)存有關(guān)

finalize()的用途何在:

使用垃圾回收器的唯一原因是為了回收程序不再使用的內(nèi)存。所以對于與垃圾回收有關(guān)的任何行為來說(尤其是finalize方法),它們也必須同內(nèi)存及其回收有關(guān)。
但是并不意味之對象中含有其他對象,finalize()就應(yīng)該明確釋放那些對象。無論對象是如何創(chuàng)建,垃圾回收器都會負(fù)責(zé)釋放對象占據(jù)的所有內(nèi)存。這就將對finalize()的需求先知道一種特殊情況,即通過某種創(chuàng)建對象方式為對象分配了存儲空間。而這種方式非java中通常的做法。這種情況主要發(fā)生在使用“本地方法”的情況下,本地方法是一種在java中調(diào)用非java代碼的方式。本地方法目前只支持C和C++,但他們可以調(diào)用其他語言寫的代碼,所以實際上可以調(diào)用任何代碼。在非java代碼中,也許會調(diào)用C的malloc()函數(shù)系列來分配存儲空間,而且出分調(diào)用了free()函數(shù),否則存儲空間將得不到釋放,從而造成內(nèi)存泄漏。當(dāng)然,free()是C和C++中的函數(shù),所以需要在finalize()中用本地方法調(diào)用它。所以要有finalize()。

必須要實施清理

要清理一個對象,用戶必須需要清理的時刻調(diào)用執(zhí)行清理動作的方法。這聽起來似乎很簡單。在C++中創(chuàng)建了一個局部對象(也就是在堆棧中,這在java中是行不通的),應(yīng)該被銷毀,但是程序員忘記調(diào)用delete()函數(shù),那么永遠(yuǎn)不會調(diào)用析構(gòu)函數(shù),這樣就會出現(xiàn)內(nèi)存泄露,對象的其他部分也不會得到清理。這種缺陷很難跟蹤。
相反,java不允許創(chuàng)建局部對象, 必須使用new創(chuàng)建對象。在java中,也沒有用于釋放對象的delete,因為垃圾回收器會幫助你釋放存儲空間。甚至可以膚淺的認(rèn)為,正是由于垃圾收集機(jī)制的存在,使得java沒有析構(gòu)函數(shù)。然而,垃圾回收器的存在并不能完全代替析構(gòu)函數(shù)。(而且絕對不能直接調(diào)用finalize(),因為finalize方法是Object類的一個Protected方法。Protected方法只能被子類內(nèi)部調(diào)用,外部不能直接調(diào)用 ,所以這也不是一種解決方案。)如果希望進(jìn)行除釋放存儲空間之外的清理工作,還是得明確調(diào)用某個恰當(dāng)?shù)姆椒?。這就等同于使用析構(gòu)函數(shù)了,只是乜有它方便。
無論是“垃圾回收”還是“終結(jié)”,都不保證一定會發(fā)生。如果java虛擬機(jī)并未面臨內(nèi)存消耗的情形,他是不會浪費時間去執(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()方法中,由于程序員的錯誤,有一本書未被簽入。如果沒有finalize()來驗證終結(jié)條件,將很難發(fā)現(xiàn)這種缺陷。
注意,System.gc()用于強(qiáng)制進(jìn)行終結(jié)動作。即使不這么做,通過重復(fù)地執(zhí)行程序(假設(shè)程序?qū)⒎峙浯罅康拇鎯臻g而導(dǎo)致垃圾回收動作的執(zhí)行),最終也能找出錯誤的Book對象。
你應(yīng)該總是假設(shè)基類版本的finalize()也要做某些重要的事情,因此要使用super來調(diào)用它,就像在Book.finalize()中看到的那樣。在本例中,它被注釋掉了,因為他需要進(jìn)行異常處理。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容