判斷對象是否已死
判斷對象是否已死就是找出哪些對象是已經死掉的,以後不會再用到的,就像地上有廢紙、飲料瓶和百元大鈔,掃地前要先判斷出地上廢紙和飲料瓶是垃圾,百元大鈔不是垃圾。判斷對象是否已死有引用計數算法和可達性分析算法。
1.引用計數算法
給每一個對象添加一個引用計數器,每當有一個地方引用它時,計數器值加 1;每當有一個地方不再引用它時,計數器值減 1,這樣只要計數器的值不爲 0,就說明還有地方引用它,它就不是無用的對象。如下圖,對象 2 有 1 個引用,它的引用計數器值爲 1,對象 1有兩個地方引用,它的引用計數器值爲 2 。
這種方法看起來非常簡單,但目前許多主流的虛擬機都沒有選用這種算法來管理內存,原因就是當某些對象之間互相引用時,無法判斷出這些對象是否已死,如下圖,對象 1 和對象 2 都沒有被堆外的變量引用,而是被對方互相引用,這時他們雖然沒有用處了,但是引用計數器的值仍然是 1,無法判斷他們是死對象,垃圾回收器也就無法回收。
2.可達性分析算法
瞭解可達性分析算法之前先了解一個概念——GC Roots,垃圾收集的起點,可以作爲 GC Roots 的有虛擬機棧中本地變量表中引用的對象、方法區中靜態屬性引用的對象、方法區中常量引用的對象、本地方法棧中 JNI(Native 方法)引用的對象。
當一個對象到 GC Roots 沒有任何引用鏈相連(GC Roots 到這個對象不可達)時,就說明此對象是不可用的,是死對象。
如下圖:object1、object2、object3、object4 和 GC Roots 之間有可達路徑,這些對象不會被回收,但 object5、object6、object7 到 GC Roots 之間沒有可達路徑,這些對象就被判了死刑。
上面被判了死刑的對象(object5、object6、object7)並不是必死無疑,還有挽救的餘地。進行可達性分析後對象和 GC Roots 之間沒有引用鏈相連時,對象將會被進行一次標記,接着會判斷如果對象沒有覆蓋 Object的finalize() 方法或者 finalize() 方法已經被虛擬機調用過,那麼它們就會被行刑(清除);如果對象覆蓋了 finalize() 方法且還沒有被調用,則會執行 finalize() 方法中的內容,所以在 finalize() 方法中如果重新與 GC Roots 引用鏈上的對象關聯就可以拯救自己,但是一般不建議這麼做,周志明老師也建議大家完全可以忘掉這個方法~
3.方法區回收
上面說的都是對堆內存中對象的判斷,方法區中主要回收的是廢棄的常量和無用的類。
判斷常量是否廢棄可以判斷是否有地方引用這個常量,如果沒有引用則爲廢棄的常量。
判斷類是否廢棄需要同時滿足如下條件:
-
該類所有的實例已經被回收(堆中不存在任何該類的實例)。
-
加載該類的 ClassLoader 已經被回收。
-
該類對應的 java.lang.Class 對象在任何地方沒有被引用(無法通過反射訪問該類的方法)。