1. 程式人生 > >java垃圾處理

java垃圾處理



1.垃圾回收


物件是使用new建立的, 但是並沒有與之相對應的delete操作來回收物件佔用的記憶體. 當我們完成對某個物件的使用時, 只需停止該物件的引用:
->將引用改變為指向其他物件
->將引用指向null
->從方法中返回, 使得該方法的區域性變數不復存在


要點:
->當我們從任何可執行程式碼都無法到達某個物件時, 它所佔用的空間就可以被回收.
->垃圾回收意味著我們永遠不用擔心出現虛懸引用(dangling reference). 虛懸引用, 指得是引用已經被刪除的記憶體空間. 在那些程式設計師可以直接控制何時刪除物件的系統中, 會存在這樣的問題.
->垃圾回收器模型: 引用計數器法(不能解決迴圈引用), 標記-清除(mark-and-sweep)


2.終結


finalize方法
->在垃圾回收器確定該物件是不可達的且該物件的空間將被回收之後, 垃圾回收器就會呼叫這個方法.
->這個方法可以清除該物件所使用的所有非記憶體資源, 對每一個物件最多隻能呼叫一次, 即使在這個方法的執行使得該物件重新變為可達之後又馬上會再次變為不可達的情況下, 該方法也只能呼叫一次.
->finalize方法可以在任何特定的時間段內被呼叫, 它也可能永遠不會被呼叫(java虛擬機器結束).


覆寫finalize方法
->當一個物件變成垃圾時, 它所引用的其他物件也很有可能會變成垃圾. 這些垃圾可能在呼叫我們編寫的finalize方法之前就已經被終結了, 因此它們可能處於不可預知的狀態.
->覆寫finalize方法是, 加上super.finalize方法. 最好加在finally字句裡面.保證其超類中宣告的部分內容也可以被終結.


3.與垃圾回收器互動的相關類和方法


類: Runtime.getRuntime(), System
方法:gc(), runFinalization(), freeMemory(), totalMemory(), maxMemory()
System類支援靜態的gc()和runFinalization()方法, 它們將呼叫當前Runtime物件上的相應方法.


4.可達性狀態和引用物件
物件只有在沒有任何引用指定它的時候才可以被當作垃圾回收, 但有時我們可能希望在仍舊有選定引用指向物件時, 將該物件作為垃圾回收掉.


引用物件的唯一用途就是維護對另一個被稱為指稱物(referent)的物件的引用. 通常我們通過欄位或者區域性變數來維護對物件的引用, 但是現在我們可以維護對引用物件的直接引用, 而該引用物件包裝了我們實際需要的物件. 垃圾回收器可能判斷出對某個物件的殘留引用是否都是經由引用物件面引用到該物件的, 因此它可以決定是否要回收該物件. 引用物件的強度將決定垃圾回收器的行為, 普通的引用都是強度最大的引用.


Reference類
->包:java.lang.ref
->典型方法: get(), clear(), enqueue(), isEnqueued()


引用和可達性強度
->物件是強可達的(strongly reachable):普通的引用
->物件是軟可達的(softly reachable):SoftReference
->物件是弱可達的(weakly reachable):WeakReference
->物件是虛可達的(phantom reachable):PhantomReference
->物件是不可達的:沒有引用連結
一旦物件變為弱可達的(或者列弱), 它就可以被終結. 如果在終結之後該物件是不可達的, 那麼它就可以被回收了.


物件可達性階段會觸發垃圾回收器對相關的引用物件型別做出適當的行為:
->軟可達物件可能會任憑垃圾回收器去回收. 我們可確定的是所有對軟可達物件的SoftReference都會在丟擲outofMemoryError錯誤這前被清除.
->弱可達物件將會被垃圾回收器回收.
->虛可達物件並不是真正意義上的可達, 因為無法通過PhantomReference訪問其指稱物件, 其get方法總是返回null. 但是虛引用的存在可以防止物件在顯式清除虛引用之前被回收. 虛引用使我們可以處理那些finalize方法已經被呼叫過的物件, 從而可以安全地認為它們是"死"的.




寫程式時候的準則:
(1)不要試圖去假定垃圾收集發生的時間,這一切都是未知的。
比如,方法中的一個臨時物件在方法呼叫完畢後就變成了無用物件,這個時候它的記憶體就可以被釋放。 


(2)Java中提供了一些和垃圾收集打交道的類,
而且提供了一種強行執行垃圾收集的方法--呼叫System.gc(),
但這同樣是個不確定的方法。Java 中並不保證每次呼叫該方法就一定能夠啟動垃圾收集,
它只不過會向JVM發出這樣一個申請,到底是否真正執行垃圾收集,一切都是個未知數。


(3)挑選適合自己的垃圾收集器。一般來說,如果系統沒有特殊和苛刻的效能要求,
可以採用JVM的預設選項。否則可以考慮使用有針對性的垃圾收集器,
比如增量收集器就比較適合實時性要求較高的系統之中。
系統具有較高的配置,有比較多的閒置資源,可以考慮使用並行標記/清除收集器。


(4)關鍵的也是難把握的問題是記憶體洩漏。
良好的程式設計習慣和嚴謹的程式設計態度永遠是最重要的,不要讓自己的一個小錯誤導致記憶體出現大漏洞。


(5)儘早釋放無用物件的引用。
大多數程式設計師在使用臨時變數的時候,
都是讓引用變數在退出活動域(scope)後,自動設定為null,
暗示垃圾收集器來收集該物件,還必須注意該引用的物件是否被監聽,如果有,則要去掉監聽器,然後再賦空值。


就是說,對於頻繁申請記憶體和釋放記憶體的操作,還是自己控制一下比較好,
但是System.gc()的方法不一定適用,最好使用finallize強制執行或者寫自己的finallize方法。