Android中GC機制的相關分析

前言java

咱們知道在Android開發中,因爲有垃圾回收機制,因此咱們不用花費太多的心思在內存分配或釋放上,可是這不是說咱們要徹底忽略。咱們知道系統分配給每一個應用程序的內存是有限的,這個內存的上限叫作「堆大小」(Heap Size),不一樣的手機,所分配的大小也不相同,可是老是有一個限度的,在開發應用程序的時候所使用的內存是不能超過這個限制的,一旦超過,就會產生OutOfMemory,所以在作緩存的時候,也要考慮到堆大小的制約。咱們這裏將要分析的GC機制,就是垃圾回收機制(Garbage Collection),其做用就是回收一些再也不使用的對象,釋放內存空間。
GC工做原理算法

GC說白了就是區分出有用的對象和無用的對象,標記無用的對象並對其進行回收。所謂「有用的對象」其實就是存在其餘的程序持有此對象的引用,反之則是無用的對象。而標記無用的對象的方法稱爲「垃圾標記算法」,可分爲「引用計數算法」和「根搜索算法」。
一、引用計數算法緩存

「引用計數算法」其實就是每個對象都維護一個內存字段來統計它被引用的數量,當對象被引用的時候它的引用計數器就加1,引用失效以後就減1,當爲0的時候,表示爲「無用的對象」,則標記爲「垃圾」待下一步的回收。這種標記方式的優勢在於它只維護局部的信息,不用掃描全局的對象圖就能夠釋放無用的對象,其效率比較高。可是如今不少主流的Java虛擬機都沒有采用這種方法來標記垃圾,主要緣由就在於它的弊端,就是引用計數算法不能解決對象之間相互循環引用的問題,以下所示:ide

//這就致使了GcObject實例1和實例2的計數引用都不爲0,不能釋放,形成內存泄漏
 public static void main(String[] args) {
     GcObject obj1 = new GcObject(); //Step1
     GcObject obj 2 = new GcObject();//Step2
     obj1.instance = obj2; //Step3
     obj2.instance = obj1;// //Step4
     obj1 = null; //Step5
     obj2 = null; //Step6
  }this

  

二、根搜索算法.net

「根搜索算法」是大多數程序語言中使用的算法,其思想就是從一個名爲「GC Roots」的對象做爲根出發點,經過引用關係遍歷對象圖,搜索過過的路徑稱爲「引用鏈」,當一個對象與GC Roots之間沒有任何的引用鏈,即從GC Roots到該對象不可到達,就斷定該對象死亡。可是咱們必須注意的是這一過程的本質是找出全部存活的對象,把其餘的對象認定爲「無用」,而不是找出死的對象回收它們佔用的空間。線程

所謂「GC Roots」實際上是一組必須活躍的引用,要實現上述的效果的前提就是必須能完整的枚舉出全部的GC Roots,不然就可能會標記「失誤」,就比如是遞歸定義的時候,如果只定義了遞推項而沒有初始項的時候,關係就沒有辦法成立,一定會出現錯誤對象

Java中可做爲GC Roots對象的有以下幾種:遞歸

    Java棧中的引用的對象
    本地方法棧中JNI引用的對象
    方法區中運行時常量池引用的對象
    方法區中靜態屬性引用的對象
    運行中的線程
    由引導類加載器加載的對象
    GC控制的對象生命週期

圖解「根搜索算法」
這裏寫圖片描述

以GC Roots爲根節點,箭頭方向爲引用方關係方向,每個藍色的原點表示的是內存當中的對象
這裏寫圖片描述
GC機制開始時,從GC Roots對象開始檢查,當它可以訪問到的對象就說明這仍是存在引用,應該進行保留,從圖中來看就是從GC Roots開始能鏈接到黃色的原點的都是有用的對象即存在「引用鏈」,其他的藍色的原點可標記爲「無用」,那麼這些無用的對象會馬上被回收嗎?即不存在「引用鏈」的對象會馬上被垃圾回收器回收嗎?咱們如今來分析一下java對象在虛擬機中的生命週期
三、Java對象在虛擬機中的生命週期

java對象被類加載器加載到虛擬機中,java對象在java虛擬機中有7個階段
一、建立階段(Created)

    爲對象分配存儲空間
    構造對象
    從超類到子類對static成員進行初始化
    遞歸調用超類的構造方法
    調用子類的構造方法

二、應用階段(In Use)
當對象被建立,並分配給變量賦值,狀態就切換到了應用階段。這一階段的對象至少要具備一個強引用,或者顯式的使用軟引用、弱引用或者虛引用。
三、不可見階段(Invisible)
程序中找不到對象的任何強引用,好比程序的執行已經超出了該對象的做用域。在不可見階段,對象仍可能被特殊的強引 用GC Roots持有着,好比對象被本地方法棧中JNI引用或是被運行中的線程引用等。
四、不可達階段(Unreachable)
程序中找不到對象的任何強引用,而且垃圾收集器發現對象不可達。
五、收集階段(Collected)
垃圾收集器已經發現對象不可達,而且垃圾收集器已經準備好要對該對象的內存空間從新進行分配時。這個時候若是該對象重寫了finalize方法,則會調用該方法。
六、終結階段(Finalized)
當對象執行完finalize法後仍然處於不可達狀態時,或者對象沒有重寫finalize方法,則該對象進入終結階段,並等待垃圾收集器回收該對象空間。
七、對象空間從新分配階段(Deallocated)
當垃圾收集器對對象的內存空間進行回收或者再分配時,這個對象就會完全消失。

由此可知不存在「引用鏈」的對象不會馬上被垃圾回收器回收,一個對象在被垃圾回收以前,至少要通過兩次標記過程,當對象和GC Roots之間不存在引用鏈時,此對象會被第一次標記爲不可到達進入收集階段,此時會進行一次篩選,若是這個對象沒有重寫finalize()方法,或者finalize()方法已經被虛擬機調用過,那麼這個對象將被放置到一個叫作F-Queue的隊列中,隊列中對象的finalize()方法將由一個虛擬機自動創建的、低優先級的Finalizer線程去執行(不等待執行結束,防止finalize()方法執行緩慢或假死)。若是finalize()執行過程當中,對象和GC Roots引用鏈又鏈接上了,好比將本身賦值給某個對象的成員變量,那麼在GC對F-Queue中對象進行第二次小規模標記的時候,這個對象將被移出「即將回收」的集合,不然進入終結階段,最終被回收

@Override  protected void finalize() throws Throwable {      super.finalize();      System.out.println("CanReliveObj finalize called !!!");      obj = this;//在finalize方法中復活對象  }  

相關文章
相關標籤/搜索