簡書:capo 轉(zhuǎn)載請注明原創(chuàng)出處,謝謝编检!
前言:
今天我們來看看Object中一個經(jīng)常被人遺忘的方法,finalize方法耕皮。老規(guī)矩撑碴,我們先看看Javadoc是怎樣描述這個方法的
/**
* Called by the garbage collector on an object when garbage collection
* determines that there are no more references to the object.
* A subclass overrides the {@code finalize} method to dispose of
* system resources or to perform other cleanup.
* <p>
* The general contract of {@code finalize} -is that it is invoked
* if and when the Java? virtual
* machine has determined that there is no longer any
* means by which this object can be accessed by any thread that has
* not yet died, except as a result of an action taken by the
* finalization of some other object or class which is ready to be
* finalized. The {@code finalize} method may take any action, including
* making this object available again to other threads; the usual purpose
* of {@code finalize}, however, is to perform cleanup actions before
* the object is irrevocably discarded. For example, the finalize method
* for an object that represents an input/output connection might perform
* explicit I/O transactions to break the connection before the object is
* permanently discarded.
* <p>
* The {@code finalize} method of class {@code Object} performs no
* special action; it simply returns normally. Subclasses of
* {@code Object} may override this definition.
* <p>
* The Java programming language does not guarantee which thread will
* invoke the {@code finalize} method for any given object. It is
* guaranteed, however, that the thread that invokes finalize will not
* be holding any user-visible synchronization locks when finalize is
* invoked. If an uncaught exception is thrown by the finalize method,
* the exception is ignored and finalization of that object terminates.
* <p>
* After the {@code finalize} method has been invoked for an object, no
* further action is taken until the Java virtual machine has again
* determined that there is no longer any means by which this object can
* be accessed by any thread that has not yet died, including possible
* actions by other objects or classes which are ready to be finalized,
* at which point the object may be discarded.
* <p>
* The {@code finalize} method is never invoked more than once by a Java
* virtual machine for any given object.
* <p>
* Any exception thrown by the {@code finalize} method causes
* the finalization of this object to be halted, but is otherwise
* ignored.
*
* @throws Throwable the {@code Exception} raised by this method
* @see java.lang.ref.WeakReference
* @see java.lang.ref.PhantomReference
* @jls 12.6 Finalization of Class Instances
*/
protected void finalize() throws Throwable { }
梳理一下上面規(guī)范講述的幾點
- 訪問垃圾收集在對象上當垃圾收集這確定這個對象上沒有更多的引用時可能會觸發(fā)finalize方法
- 一個子類重寫了finalize方法處理系統(tǒng)資源或者執(zhí)行其它的清理也就是調(diào)用System.gc可能會觸發(fā)
- 這個一般的合約規(guī)定finalize,如果Java虛擬機已經(jīng)確定這不再有任何意味這個對象能通過任何一個尚未死亡的線程訪問的方法調(diào)用仑濒,除非是由于最后確定的其它對象或類的準備工作所采取的行動.
- finalize方法可以采取任何行動,包括使此對象再次可用于其它線程,finalize的通常是在對象不可撤銷地丟棄之前執(zhí)行清楚動作.例如:表示輸入輸出連接的對象的finalize方法可能會在對象被永久丟棄之前執(zhí)行顯式I/O事務來中斷連接
Object中finalize操作只是返回一個正常線程 - Java規(guī)范確保調(diào)用finalize的線程在調(diào)用finalize方法時不會持有任何用戶可見的同步鎖
- 如果finalize方法拋出未捕獲的異常,則會忽略該異常,并終止該對象的定類。
- finalize方法不會被任何對象調(diào)用多次
- finalize方法拋出的任何異常都會導致該對象的終止被停止,否則就忽略
針對上述規(guī)范,我有以下幾點疑惑
finalize到底是使用在什么應用場景
通常我們知道對象創(chuàng)建完,要做清理操作,當然GC會清理哪些由new分配的內(nèi)存,但是如果不是通過new分配的內(nèi)存清理的話,就需要調(diào)用finalize方法.
它的工作原理:一旦垃圾回收器準備釋放對象占用的內(nèi)存空間,將首先調(diào)用finalize方法,并且在下一次垃圾回收動作發(fā)生,才會真正的回收該對象占用的內(nèi)存,也就是finalize方法會在垃圾回收器真正回收對象之前調(diào)用
finalize到底是不是C++中的析構(gòu)函數(shù)
在C++中銷毀對象必須用到析構(gòu)函數(shù),且兌現(xiàn)一定會被銷毀(如果程序沒有缺陷)偷遗,但是Java里的對象并非總是被回收.也就是:
- 對象有可能沒有被垃圾回收
- 垃圾回收并不是等于析構(gòu)(因為析構(gòu)一定會銷毀對象)
我們使用finalize這個方法有時候是為了確保某個對象已經(jīng)被清楚墩瞳,我們自己手動的去清除這個對象.
一般只要程序沒有瀕臨儲存空間用完的那一刻,對象占用的存儲空間就總也得不到釋放.如果這個程序結(jié)束了,垃圾回收器一直沒有釋放掉你創(chuàng)建的任何對象的存儲空間,則隨著程序的退出,那些資源也會全部交還給操作系統(tǒng).個人覺得這個操作策略是正確的,能不用垃圾回收去處理就不出來,畢竟就省去了這部分開銷。
所以finalize并不會確保對象會被銷毀,它不是析構(gòu)函數(shù).
finalize另外一個用途(判斷對象在清理時是否安全釋放)
前面已經(jīng)說過它可能是用來清理一些不是用過new創(chuàng)建的內(nèi)存對象,那么這些對象就是指的是Java中的本地方法創(chuàng)建的對象
無論是垃圾回收還是終結(jié)方法(finalize)都不能不保證一定會發(fā)生,因為它們發(fā)生的條件是對象被回收(這個條件是比較苛刻的氏豌,JVM只有在瀕臨內(nèi)存耗盡的情況下才會觸發(fā))
finalize可能會用于對象終結(jié)條件的驗證:
我們可以用finaliz來檢測對象被清理之前是不是還有什么操作沒有執(zhí)行,比如當某個對象打開了一個文件,在對象被回收之前程序應該關閉這個文件喉酌。使用finalize我們可以確保對象對安全的釋放掉了.
下面看finalize一個用于檢驗對相關是否安全釋放的例子:
package com;
/**
* Created by XMl on 2017/9/8.
*/
public class Book {
public boolean checkedOut = false;
public Book(boolean checkedOut) {
// checkedOut = checkedOut;
this.checkedOut = checkedOut;
}
public void checkIn() {
checkedOut = false;
}
/**
* 重寫finalize方法
*
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
//校驗checkedOut
if (checkedOut) {
System.out.println("校驗出現(xiàn)了一次錯誤: Checked out ");
}
// super.finalize();
}
}
我們寫一測試類
package com.rtti.xml;
import com.Book;
/**
* Created by XMl on 2017/9/8.
*/
public class TerminationCondition {
public static void main(String[] args) {
Book novel = new Book(true);
novel.checkIn();
//對checked進行了一次誤操作
new Book(true);
//調(diào)用Gc 強制執(zhí)行終結(jié)操作(finalize)
System.gc();
}
}
打印信息:
我們看到輸出校驗信息.但是我們調(diào)用System.gc的時候并沒有回收對象內(nèi)存
總結(jié):
- 垃圾回收內(nèi)存是JVM的一個不確定操作,通常會在系統(tǒng)瀕臨內(nèi)存溢出可能會回收,所以你不要指望垃圾回收來控制finalize方法的調(diào)用
- finalize方法通常是用于清理一些非本地方法(native),和一些對象安全釋放校驗的操作