01.09 在java中為什麼不推薦使用finalize,看到這個原因你也不會用了

java提供了一個finalize方法,可以幫助我們進行資源釋放,類似於C++中的析構函數。但是目前普遍的認識是不要使用,為什麼呢?就是因為對java虛擬機的垃圾回收有影響。這篇文章對其進行一個說明。

一、為什麼有影響

我們都知道一個對象如果沒有了任何引用,java虛擬機就認為這個對象沒什麼用了,就會對其進行垃圾回收,但是如果這個對象包含了finalize函數,性質就不一樣了。怎麼不一樣了呢?

java虛擬機在進行垃圾回收的時候,一看到這個對象類含有finalize函數,就把這個函數交給FinalizerThread處理,而包含了這個finalize的對象就會被添加到FinalizerThread的執行隊列,並使用一個鏈表,把這些包含了finalize的對象串起來。

在java中為什麼不推薦使用finalize,看到這個原因你也不會用了

他的影響在於只要finalize沒有執行,那麼這些對象就會一直存在堆區,不過這裡只是4個包含了finalize的對象,影響不是那麼大,如果有一萬個或者是十萬個呢?這就影響大了。

finalize的原理其實很簡單,在這裡簡要的梳理一下:

(1)對象在初始化的過程中會判斷是否重寫了finalize,方法是判斷兩個字段標誌has_finalizer_flag和RegisterFinalizersAtInit。

(2)如果重寫了finalize,那就把當前對象註冊到FinalizerThread的ReferenceQueue隊列中。註冊之後的對象就叫做Finalizer。方法是調用register_finalizer函數。此時java虛擬機一看當前有這個對象的引用,於是就不進行垃圾回收了。

(3)對象開始被調用,FinalizerThread線程負責從ReferenceQueue隊列中獲取Finalizer對象。開始執行finalize方法,在執行之前,這個對象一直在堆中。

(4)對象執行完畢之後,將這個Finalizer對象從隊列中移除,java虛擬機一看對象沒有引用了,就進行垃圾回收了。

這就是整個過程。不過在這裡我們主要看的是finalize方法對垃圾回收的影響,其實就是在第三步,也就是這個對象含有finalize,進入了隊列但一直沒有被調用的這段時間,會一直佔用內存。

我們使用一個案例來分析一波:

二、案例演示

我們創建一個類

在java中為什麼不推薦使用finalize,看到這個原因你也不會用了

現在創建了類,我們設置一下參數。

在java中為什麼不推薦使用finalize,看到這個原因你也不會用了

在main方法中,創建了1000個Fdd對象,如果不執行finalize方法,那麼因為沒有調用所以會進行垃圾回收,此時不斷我們創建多少個,都不會出現任何問題。但是如果存在finalize方法,就不一樣了。

在java中為什麼不推薦使用finalize,看到這個原因你也不會用了

我們看到每個對象都會執行finalize,在執行之前的這段時間一直會在堆區,執行完了就會被清理,所以你看到這裡執行了不少於5次的finalize方法。但是對象一旦超出了我們設置的5M,就會出現內存溢出。一句話總結就是出現了對象堆積。現在使用MAT工具來分析一下。

Mat工具是一個插件,也可以自己下載一個。下載完成之後打開我們剛剛生成的a.dump即可。

下面這張圖就是分析的結果:

在java中為什麼不推薦使用finalize,看到這個原因你也不會用了

a這塊的內容就是Finalizer,也就是我們的Fdd對象,b包含的比較多,亂七八糟的剩餘信息。當然你也可以查看一些其他的信息。都在MAT工具上。還有一些正在執行的finalizer和準備執行的。

在java中為什麼不推薦使用finalize,看到這個原因你也不會用了

OK,一些其他的信息就不再展示了。這裡參考了很多博客還有書籍,但是沒有抄一個字。例子也是我自己寫的,特在此說明。


分享到:


相關文章: