在Java中,程序員不需要去關(guān)心內(nèi)存動態(tài)分配和垃圾回收的問題,這一切都交給了JVM來處理。
一、如何確定需要GC回收的對象?
所謂的要“回收的垃圾”指的是那些不可能再被任何途徑使用的對象。那么如何找到這些對象?
1. 引用計數(shù)法
在java中是通過引用和對象進(jìn)行關(guān)聯(lián)的,也就是說如果要操作對象,必須通過引用來進(jìn)行。所以,可以通過引用計數(shù)來判斷一個對象是否可以被回收。如果一個對象沒有被任何引用所引用,則說明該對象基本不太可能在其他地方被使用到,那么這個對象就成為可被回收的對象了。這種方式成為引用計數(shù)法。
但是,Java中卻沒有使用這種算法,因為這種算法很難解決對象之間相互引用的情況。
2. 可達(dá)性分析算法
這個算法的基本思想是通過一系列稱為“GC Roots”的對象作為起始點,從這些節(jié)點向下搜索,搜索所走過的路徑稱為引用鏈,當(dāng)一個對象到GC Roots沒有任何引用鏈(即GC Roots到對象不可達(dá))時,則證明此對象是不可用的。
那么如何選取GCRoots對象呢?在Java語言中,可以作為GCRoots的對象包括下面幾種:
(1). 虛擬機(jī)棧(棧幀中的局部變量區(qū),也叫做局部變量表)中引用的對象。
(2). 方法區(qū)中的類靜態(tài)屬性引用的對象。
(3). 方法區(qū)中常量引用的對象。
(4). 本地方法棧中JNI(Native方法)引用的對象。
二、四種引用狀態(tài)
1. 強(qiáng)引用
代碼中普遍存在的類似"Object obj = new Object()"這類的引用,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會回收掉被引用的對象。
2. 軟引用
描述有些還有用但并非必需的對象。在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會把這些對象列進(jìn)回收范圍進(jìn)行二次回收。如果這次回收還沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常。Java中的類SoftReference表示軟引用。
3. 弱引用
描述非必需對象。被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾回收之前,垃圾收集器工作之后,無論當(dāng)前內(nèi)存是否足夠,都會回收掉只被弱引用關(guān)聯(lián)的對象。Java中的類WeakReference表示弱引用。
4. 虛引用
這個引用存在的唯一目的就是在這個對象被收集器回收時收到一個系統(tǒng)通知,被虛引用關(guān)聯(lián)的對象,和其生存時間完全沒關(guān)系。Java中的類PhantomReference表示虛引用。
對于可達(dá)性分析算法而言,未到達(dá)的對象并非是“非死不可”的,若要宣判一個對象死亡,至少需要經(jīng)歷兩次標(biāo)記階段。
如果對象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒有與GCRoots相連的引用鏈,則該對象被第一次標(biāo)記并進(jìn)行一次篩選,篩選條件為是否有必要執(zhí)行該對象的finalize方法,若對象沒有覆蓋finalize方法或者該finalize方法已經(jīng)被虛擬機(jī)執(zhí)行過了,則均視作不必要執(zhí)行該對象的finalize方法,即該對象將會被回收。反之,若對象覆蓋了finalize方法并且該finalize方法并沒有被執(zhí)行過,那么,這個對象會被放置在一個叫F-Queue的隊列中,之后會由虛擬機(jī)自動建立的、優(yōu)先級低的Finalizer線程去執(zhí)行,而虛擬機(jī)不必要等待該線程執(zhí)行結(jié)束,即虛擬機(jī)只負(fù)責(zé)建立線程,其他的事情交給此線程去處理。
對F-Queue中對象進(jìn)行第二次標(biāo)記,如果對象在finalize方法中拯救了自己,即關(guān)聯(lián)上了GCRoots引用鏈,如把this關(guān)鍵字賦值給其他變量,那么在第二次標(biāo)記的時候該對象將從“即將回收”的集合中移除,如果對象還是沒有拯救自己,那就會被回收。
對象的finalize方法最多被虛擬機(jī)調(diào)用一次。