淺談 Java 之 GC

閱讀本文假設你對java內存模型已有一些瞭解。java

一、Java虛擬機中哪些內存須要回收?

  先來看看jvm內存模型,以下圖算法

   

  線程隔離的區域隨線程而生,隨線程而滅;程序計數器可保存着虛擬機字節碼指令的地址(能夠看作是當前線程所執行的字節碼的行號指示器);棧中的棧幀(與方法關聯)隨着方法的進入和退出執行壓棧出棧操做。既然每個棧幀與方法關聯,那每一個棧幀分配多少內存基本上在類結構肯定下來就已知。能夠看出線程隔離的區域內存的分配和回收具備肯定性,當方法結束或線程結束時,內存天然就進行回收了,不須要咱們去考慮回收的問題。而在java堆和方法區來講,一個接口可能有多個實現類,咱們只有在程序運行期間才知道建立了哪些對象,也即在運行時動態分配內存,而且是否回收是根據對象是否還可用進行回收判斷的,java的GC關注的就是這部分的內存。jvm

二、何時回收? 

  當對象已經「死亡「(即不可能再被任何途徑使用的對象)。spa

  那麼判斷對象是否存活的算法有哪些?線程

       (1)引用計數算法:給對象添加一個引用計數器,每當有一個地方引用它,計數器加1;引用失效時,計數器減1,任何計數器爲0的對象就是不可能再被使用的。該算法實現簡單,判斷效率高,可是Java虛擬機裏並無選用該算法來管理內存,由於該算法很難解決對象相互循環引用問題(相互引用的對象除了它們倆之間沒有其餘別的引用)。orm

       (2)可達性分析算法:經過一系列的「GC Roots」做爲對象的起點,從這些節點開始向下搜索,搜索走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連(即不可達),則該對象是不可用的。以下圖對象五、6爲可回收對象。對象

  

  

  在Java中,可做爲GC Roots 的對象包括如下blog

    1) 虛擬機棧(棧幀中的本地變量表)中引用的對象;接口

    2) 方法區中類靜態屬性引用的對象;內存

    3) 方法區中常量引用的對象;

    4) 本地方法棧中JNI(Java Native Interface)引用的對象。

  可是,請注意,不可達對象並不是是「非死不可」,宣告對象「死亡」要通過至少兩次標記過程,沒有引用鏈到達GC Roots標記第一次,並進行篩選,篩選的條件是此對象是否有必要執行finalize()方法。當對象沒有重寫finalize()方法時或finalize()已被虛擬機調用過,虛擬機將這兩種狀況視爲沒有必要執行。Finalize() 方法是對象自我拯救的最後機會,資源從新與引用鏈上的任何一個對象創建關聯,在第二次標記它將被移除出即將回收的集合。

  方法區上的回收時機

  垃圾回收包含兩部份內容:廢棄常量和無用的類。

  廢棄常量:與java堆對象回收類似,沒有再被任何對象引用,就會被回收;

  無用的類:知足如下條件,即爲無用類,可被回收。

    1) 該類的全部實例都已經被回收,java堆中不存在該類的任何實例;

    2) 加載該類的ClassLoader已經被回收;

    3) 該類對應的java.lang.Class對象沒有在任何地方被引用,沒法在任何地方經過反射訪問該類的方法。

三、垃圾回收算法

  (1)標記-清除算法:算法分爲「標記」和「清除」兩個階段,首先標記出全部要回收的對象,而後統一回收被標記的對象,因爲死亡的對象大部分不是連續的,該算法會較多的內存碎片,在下一次對較大對象分配內存時可能會由於沒法分配足夠大的連續空間而再次進行垃圾回收;

       (2)複製算法:將內存進行分區,一塊區域用完就進行垃圾回收,把存活對象複製到另外一塊區域,而後清空原來區域。在新生代中就是使用複製算法,把內存以8:1:1分配,分別稱爲Eden,From Survivor,To Survivor,至於爲何要8:1:1分配,能夠參考如下兩個連接  http://ifeve.com/jvm-yong-generation/   https://dsxwjhf.iteye.com/blog/2201687

       (3)標記-整理算法:與標記清理類似,但後續不是直接清理而是先移到一端後在進行清理,避免了該內存裏的內存碎片產生。

       (4)分代回收算法:新生代使用複製算法,老年代使用標記-整理算法

 

 

書暫時看到這部分,下次再來說講垃圾回收算法在jvm的基本實現及具體的垃圾回收器。

相關文章
相關標籤/搜索