深刻理解java虛擬機之判斷Java對象存活算法

  Java一個重要的優點就是經過垃圾管理器GC (Garbage Collection)自動管理和回收內存,程序員無需經過調用方法來釋放內存。也所以很好多的程序員可能會認爲Java程序不會出現內存泄漏的問題,這種想法是不對的,當咱們對內存使用不當的時候仍然可能會出現內存泄漏,而且問題相對與c++來講更隱祕,問題的根源排查起來也比較困難。不過,當咱們瞭解了Java虛擬機內存區域,Java垃圾收集器以後,對於解決內存泄漏的問題也就不是什麼困難的事情了。 html

  相關閱讀java

  一、深刻理解java虛擬機之java內存區域c++

 

  在前一篇博客中已經已經詳細分析了java內存運行時各個區域,其中程序計數器、虛擬機棧、本地方法棧隨着線程而生,隨着線程而滅亡,操做數棧中的棧幀隨着方法的執行的有條不紊地進行着入棧和出棧的操做,每一個棧幀中分配多少內存基本上在類結構肯定下來時就已經肯定了大小了,所以這幾個區域的內存分配和回收都具有肯定性,在這個幾個區域中就不須要過多的考慮內存分配的問題了,由於隨着線程的結束,內存在然而然地就已經被回收了。而java堆和方法區不同,一個接口中多個實現類所須要的內存可能不同,一個方法中多個分支須要的內存的大小也有多是不一樣的,咱們在程序的運行期才能肯定哪些對象須要建立,建立所需內存是多大,這些內存都是動態分配的,垃圾回收器考慮的就是這部分的內存,接下來咱們將圍繞如下3個問題來展開描述java垃圾收集器是如何自動回收內存的:程序員

  一、哪些內存須要回收?算法

  二、何時回收?線程

  三、如何回收?視頻

  本片博客就圍繞這第一個問題展開說明,其他連個問題將在後面的博客中一一詳解;htm

  首先,Java垃圾回收器的一個想要解決的問題是什麼樣的內存須要被回收呢?Java垃圾收集器認爲,當一個對象再無被其它對象引用時能夠認爲這個對象能夠回收了。那麼垃圾收集器是怎麼知道對象的死亡的仍是存活的呢?目前,Java虛擬機有兩種算法來肯定哪些對象是無用的,須要被回收的。對象

  1.引用計數法

  該算法的思路是給每一個對象都添加一個引用計算器,每當有其它對象引用時計數器就+1,當引用失效時計數器-1,任什麼時候刻當該對象的引用數爲0的時候,則斷定這個對象不會再被使用了,能夠將該對象回收了。這種算法實現起來很簡單,效率也很是高,可是並無被Java所採用,緣由是這種算法很難解決對象相互引用的問題。看一下下面例子的代碼:blog

public class Test {

    private Object instance = null;
    private static final int _1MB = 1024 * 1024;

    /** 這個成員屬性惟一的做用就是佔用一點內存 */
    private byte[] bigSize = new byte[2 * _1MB];

    public static void main(String[] args)
    {
        Test A= new Test();
        Test B = new Test();
        A.instance = B;
        B.instance = A;
        A = null;
        B = null;

        System.gc();
    }
}

  運行結果:

[GC (System.gc())  9299K->720K(249344K), 0.0010947 secs]
[Full GC (System.gc())  720K->627K(249344K), 0.0075042 secs]

  上面的例子中A和B相互引用,它們的引用計數並不爲0,可是執行System.gc()方法後仍然被回收了,這說明了Java垃圾收集器並非使用引用計數法的。

  2.可達性分析算法

  可達性分析算法就是Java垃圾收集器判斷‘垃圾’對象的算法。基本思路是經過一系列的」GC Roots「 的對象做爲起點,從這些節點開始向下搜索,搜索所走過的路程叫作引用鏈,當一個對象沒有任何引用鏈與」GC Roots「有連接時,那麼能夠斷定這個對象是無用的對象。能夠做爲GC Roots對象的包括一下幾種:

  1)Java虛擬機棧中局部變量表引用的對象;

  2)本地方法棧中JNI所引用的對象;

  3)方法區中的靜態變量;

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

  下圖爲GC Roots:

  圖中obj1 ~ obj7都可以直接或間接地與GC Roots有鏈接,所以他們是存活對象,不會被GC回收。obj8基本上能夠被回收了,obj9和obj10雖然有相互引用,可是他們的引用鏈中並無達到GC Roots,所以也會斷定爲非存活對象。

  3.引用的四種狀態

  在jdk1.2以前,Java的引用類型是比較簡單的,只有被引用和未引用兩種狀態,類型過於簡單的話不利於垃圾收集器的回收,當已經將未被引用的對象都被回收以後內存仍然比較緊張時,垃圾收集器將沒法確認被引用的對象是否須要回收,哪些對象能夠被回收等。jdk1.2後Java對引用類型進行的拓展,包括:強引用、軟引用、弱引用和虛引用四種狀況,四者的引用強度依次遞減。這樣在虛擬機中內存使用不一樣的狀況下,分別回收不容引用類型的對象。

  1)強引用

  在程序中咱們直接new出來的對象的引用都屬於強引用,好比:Object obj = new Object();只要強引用還在,就不會被GC回收。

  2)軟引用

  描述部分部分有用但非必須的對象。在系統將要發生內存溢出的時候,虛擬機會嘗試從這部分的引用類型中進行二次回收,若是二次回收後的內存仍不夠用纔會拋出內存溢出異常。Java中的類SoftReference表示軟引用。

  3)弱引用

  描述被必須對象。被弱引用關聯的對象只能生存到下一次回收以前,在垃圾收集器工做以後,不管內存是否夠用,這類的對象都會被回收掉。Java中的類WeakReference表示弱引用。

  4)虛引用

  被虛引用關聯的對象在被回收時會收到系統的通知。被虛引用關聯的對象,和其生存時間徹底不要緊。Java中的類PhantomReference表示虛引用。

 

  對於可達性分析算法,對於那些沒有與GC Roots關聯的對象並不是是當即回收的,而是經歷過兩次標記後仍然沒有與GC Roots關聯上,此時纔會回收該對象。對象在進行可達性分析後若是沒有與GC Roots關聯,則會進行第一次標記和第一次篩選,篩選條件爲是否有必要執行finalize方法,好比若是finalize方法是否被覆蓋,或是否已被執行一次。若是沒有被覆蓋或已被執行了,基本能夠確認該對象會被回收了。不然,將這個對象放到F-Queue隊列中。對F-Queue隊列二次標記,若是在此次標記中對象成功關聯上GC Roots,則該對象拯救了本身,將從」即將回收「的集合中移除,不然將會被回收。然而,對象只能拯救本身一次,第二次就會被回收了。

迎你們關注公衆號: 【java解憂雜貨鋪】,裏面會不定時發佈一些技術乾貨博客;關注便可免費領取大量最新,最流行的技術教學視頻:

 

 

相關文章
相關標籤/搜索