一.Java的垃圾回收機制
Java的垃圾回收機制(java garbage collection)是Java虛擬機提供的能力,用於在空閑時間以不定時的方式動態回收無任何引用的對象占據的堆內存空間。
注意粗體字的地方,java的垃圾回收線程是優先級比較低的線程,什么時候進行垃圾回收難以確定。當某些對象被標記為垃圾對象后,等垃圾回收線程運行時,就會將這些對象回收(確切的說應該是回收這些對象所占的堆內存空間)。
二.什么樣的對象會被標記成垃圾對象呢?
- 一般來說,所有指向對象的引用都已失效,不可能再有程序能調用到這個對象,那么這個對象就成了垃圾,應該被回收。
- 根據這個思路,很容易就能想到用《引用計數》的辦法來確定一個對象是否是垃圾。即每當多一個引用指向對象時,引用計數加一,每當少一個引用指向對象時,引用計數減一,引用計數減到零,對象就可以被回收了。
- 然而引用計數有一個致命問題不好解決,就是循環引用的問題。比如說一個循環鏈表,他們循環引用者,引用計數永遠不會為零,但是實際上程序已經不能訪問他們了,他們應該被回收。(如果還沒明白循環引用,第五步之后有代碼說明)
- 所以Java實際上是使用基於GC Roots的可達性分析,什么是GC Roots?所有類的靜態變量,每個線程調用棧上的本地變量(實際上我們編程時也是要從這些地方開始訪問數據),所有這些對象,以及被這些對象所指向的對象,都是活的對象。活的對象所指向的對象也是活的對象。GC通過有向圖的進行可達性分析,不可達的對象就被視為是垃圾對象。
- 所以只要在GC的時刻,讓程序暫停運行,然后從GC Roots開始分析,最后沒有被標記為活對象的對象就是垃圾了。
*代碼事例解釋什么是循環引用:

如上圖所示,假設我們有兩個類分別是A和B,A類中有一個字段是B類的類型,B類中有一個字段是A類類型,現在分別new一個A類對象和new一個B類對象,此時引用a指向剛new出來的A類對象,引用b指向剛new出來的B類對象,然后將兩個類中的字段互相引用一下,這樣即使下面進行a = null和b = null,但是A類對象仍然被B類對象中的字段引用着,盡管現在A類和B類獨享都已經訪問不到了,但是引用計數卻都不為0.
三.Java中有哪幾種引用
- 強引用(StrongReference):強引用就是我們平時使用的引用,是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它(結合上面,一個對象有強引用,那么這個對象一定可達,也就是說明這個對象是活的)。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。(簡記:永不回收)
- 軟引用(SoftReference):如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。所以,軟引用可用來實現內存敏感的高速緩存。(簡記:內存不足再回收)
- 弱引用(WeakReference):弱引用與軟引用的區別在於:弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由於垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。(簡記:遇到就回收)
- 虛引用(PhantomReference):“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃圾回收器准備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。(簡記:形同虛設)
值得區分的一點是,軟引用和弱引用是可選的時候和引用隊列聯合使用,如果與引用隊列聯合使用,那么當所引用的對象被垃圾回收,Java虛擬機就會把這個弱(或軟)引用加入到與之關聯的引用隊列中。而虛引用是必須和引用隊列聯合使用。
