歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

JVM內存回收對象及引用分析

自動垃圾回收是Java相較於C++的一個重要的特點,想了解JVM的垃圾回收機制,首先我們要知道垃圾回收是回收什麼地方的垃圾,我在我的上一篇文章《JVM內存區域劃分》裡面有寫到JVM裡面的內存是怎麼分布的,這裡的回收主要是指對上文中提到的Java堆和方法區的內存的回收。

什麼樣的對象可以被回收

知道了回收哪裡的內存之後,我們還需要知道什麼樣的對象是可以被回收,或者說是需要被回收的,這些對象我們稱之為死掉的對象。那麼哪些對象是死掉了的呢?我們說當一個對象不存在任何引用的時候就可以說這個對象是死掉了。

那麼什麼時候這個對象不存在任何引用了呢?

有一些地方說,可以使用引用計數算法來判斷對象是否還存活,引用計數算法是說給對象添加一個引用計數器,每當一個地方引用它時就加1,引用失效時就減1,計數器的值變為0就說這個對象不存在任何引用了,但是這樣會存在一個很嚴重的問題,就是循環引用的問題。比如如下的例子:

public class Example{
  public Object instance = null;

  publicstaticvoidtest(){
    Example objA=new Example();
    Example objB=new Example();

    objA.instance=objB;
    objB.instance=objA;

    objA=null;
    objB=null;
  }
}

這樣的話,objA和objB的引用計數都不為0,但是他們的確是不會再被使用的了。

那麼Java裡面是用什麼樣的算法來實現對引用的判斷的呢?

Java裡是使用可達性分析的方法來實現的。

如圖所示,就是通過一系列的稱為"GC Roots"的對象作為起始點,從這些點向下搜索,搜索的路徑稱為引用鏈,當一個對象到達GC Roots沒有任何引用鏈時,就可以證明這個對象是不可用的。那麼這個對象就可以被回收了。像圖中的Obj1-5都屬於存活的對象,但是Obj6-8雖然還存在相互引用,但是已經是可以認為是死掉的對象了。

引用

目前來說,Java中的引用可以分為強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)四種,他們的引用順序依次逐漸減弱。

  • 強引用是用來描述必須存在和引用的對象,比如Object a=new Object(),只要強引用還存在,被引用的對象就永遠不會被回收。
  • 軟引用是用來描述一些還有用但是不是必需的對象。這類引用的對象會在內存溢出之前被列入回收范圍進行第二次回收,如若回收完之後還沒有獲得足夠的內存才會拋出內存溢出異常。可以使用SoftReference類來實現。
  • 弱引用是用來描述非必需的對象,但是強度比軟引用更弱,被引用的對象只能存活到下一次GC之前。當進行GC的時候不論內存是否足夠都會對該引用的對象進行回收。可以使用WeakReference類來實現。
  • 虛引用是最弱的引用關系。虛引用的存在不會對對象的存活造成任何影響,也不能通過虛引用來獲得任何對象實例。設置虛引用的唯一目的就是在關聯對象被回收時會獲得一個系統通知。可以使用PhantomReference類來實現。

死亡對象的自我救贖

當一個對象在進行可達性分析的時候發現已經是沒有任何引用的了,這時候垃圾收集器並沒有立即判處該對象死刑,而是給了它一次自我救贖的機會,這時它會被標記一次,同時判斷是不是有必要執行finalize()方法,當對象沒有重寫finalize()方法或者finalize()方法已經被執行過的時候,就不執行finalize()方法。下面就會有兩種情況發生了:

  • 執行finalize()方法:這時這個對象會被放在F-Queue隊列之中,然後由虛擬機自動創建一個低優先級的Finalizer線程去執行它,但是為了保證F-Queue不被無限阻塞並不保證一定會等待finalize()方法執行結束。此時在finalize()裡如果對象又重新獲得與GC Roots的聯系就可以完成自我救贖,否則的話就只能被第二次標記,然後聽天由命了。
  • 不執行finalize()方法:這時候要麼是垃圾收集器已經給過一次機會了,要麼是對象自己沒有重寫finalize()去獲得機會,這個時候就只能被第二次標記,立即被執行死刑了。

方法區的回收

方法區的回收分為廢棄常量的回收和無用類的卸載。

  • 廢棄常量的回收:回收廢棄常量與上面所說的堆中對象的回收差不多,沒有任何引用的時候回收。
  • 無用類的卸載:類的卸載需要滿足三個條件:
  1. 該類的所有對象都被回收。
  2. 加載該類的ClassLoader已經被回收。
  3. 該類對應的java.lang.Class沒有在任何地方被引用,無法在任何地方通過反射訪問該類。
    但是滿足這三個條件只是說可以被回收,但是不代表一定被回收,具體是否被回收還要由虛擬機裡的一些參數來具體確定。

 

Copyright © Linux教程網 All Rights Reserved