JVM進階之GC(二)對象存活判斷算法

上一篇簡單的說明了對象在JVM中的內存分代策略,此文繼續來爲GC打基礎,說說什麼樣的對象需要被GC,即對象是否存活判定算法。

判定對象存活算法

如何判斷對象淪爲了垃圾也是門技術。

引用計數法

引用計數法就是給對象加個引用計數器,每有一個地方引用到它時,這個引用計數器就加1。當引用失效的時候,計數器的值就減1,也就是說根據引用計數器的值來判斷對象是否存活,若值是0,那麼該對象就不再被使用了。 
真是如此嗎?答案是否定的。試想一下如下圖的場景: 
對象互相引用 
AB對象相互引用,那麼AB對象的引用計數器的值永遠都不會爲0,AB就永遠都不會被回收,直接造成內存泄漏問題。所以該算法最大的缺點是很難解決對象之間相互引用的問題。但事實上,如上圖情況的相互引用對象會被回收,說明實際不是用引用計數法判定對象存活與否。

可達性分析法

可達性分析法的基本思路是通過一系列的GC Roots對象作爲起始點,從這些點向下搜索它們引用的對象,這樣可以生成一顆引用樹,樹的節點就是可達的對象。反之,不在樹上的對象即可判定對象已死。來看如下圖: 
可達性分析法 
D和E對象相互引用,但是它們沒有RC Roots根節點的引用指向,所以D和E在下次垃圾回收時會被處死,而ABC對象在GC Roots的引用樹上,會視爲存活對象。

那麼哪些對象可以作爲引用樹的根節點呢?

可作爲GC Roots的對象

不難想象,作爲GC Roots的對象必須是極難被回收的對象,包括瞭如下幾種對象:

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象,如在方法中定義和使用的變量
  • 方法區中的類靜態屬性引用的對象,如static修飾的成員變量
  • 方法區中常量引用的對象,如static和final共同修飾的常量
  • 本地方法棧中JNI引用的對象,JNI也就是調的native方法

引用類型與垃圾回收時機

不論是通過以上哪種方式判斷對象是否存活,都與引用相關。java自1.2之後,對引用劃分了4種,如下:

  • 強引用:只要某個對象有強引用與之關聯,JVM必定不會回收這個對象,即使在內存不足的情況下,JVM寧願拋出OutOfMemory錯誤也不會回收這種對象。

  • 軟引用:軟引用是用來描述一些有用但並不是必需的對象,比如引用圖片地址等,在Java中用java.lang.ref.SoftReference類來表示。對於軟引用關聯着的對象,只有在內存不足的時候JVM纔會回收該對象。

  • 弱引用:弱引用也是用來描述非必需對象的,當JVM進行垃圾回收時,無論內存是否充足,都會回收被弱引用關聯的對象。也就是說被弱引用關聯的對象,只能生存到下一次垃圾收集發生之前

  • 虛引用(幽靈引用或幻影引用):虛引用和前面的軟引用、弱引用不同,它並不影響對象的生命週期。在java中用java.lang.ref.PhantomReference類表示。如果一個對象與虛引用關聯,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收。爲一個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知。與弱引用區別:在GC時會被通知。

好了,今天點到爲止,下篇談談垃圾回收算法和垃圾回收器。