深刻學習Java虛擬機(二)

爲何學習垃圾回收機制?java

    當須要排查各類內存溢出,內存泄露問題時,當垃圾收集成爲系統達到更高併發量的瓶頸時,咱們就須要對這些「自動化」的技術實施必要的監控和調節。算法

GC算法緩存

    引用計數法、標記清除法、標記壓縮法、複製算法、併發


引用計數法高併發

    是什麼?:給對象中添加一個引用計數器,每當有一個地方引用此對象,計數器值加1;當引用失效時,計數器值減1;任什麼時候刻計數器值爲0的對象就是不可能再被使用的。學習

    優勢:實現簡單,斷定率也很高。spa

    缺點:很難解決對象之間的相互循環引用。對象

可達性分析法隊列

    是什麼:經過「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到「GC Roots」沒有任何引用鏈相連時,則證實此對象不可用。內存


    再談引用

        JDK1.2 以前,Java中的引用定義很傳統:若是reference 類型的數據中存儲的數值表明的是另一個內存的起始地址,就稱爲這塊內存表明着一個引用。

        咱們但願可以定義描述這樣一類對象,內存空間還足夠時,則能保留在內存之中;若是內存空間在進行垃圾收集後仍是很是緊張則能夠拋棄這些對象。(例如緩存)

        4種不一樣強度的引用對象:強引用、軟引用、弱引用、虛引用。強度依次逐漸減弱。

        強引用:Object obj = new Object( ) ;

        軟引用:描述一些還有用可是非必需的對象。對於軟引用關聯着的對象,在系統將要發生內存溢出異常以前,將會把這些對象列進回收範圍之中進行二次回收。

        弱引用:描述非必需對象,比軟引用更弱,只能生存島下一次垃圾收集以前。當垃圾收集器工做時,不管當前內存是否足夠,都會回收掉只被弱引用關聯的對象。

        虛引用:徹底不會對其生存時間構成影響,也沒法經過虛引用來取得一個對象實例。爲一個對象設置虛引用關聯的惟一目的就是能在這個對象被收集器回收的時候收到一個系統通知。

    生存仍是死亡

        即便在可達性分析算法中不可達的對象,也並不是「非死不可」。真正宣告一個對象的死亡,至少要經歷兩次標記過程:       

            1:對象進行可達性分析後沒有發現與GC Roots相連的引用鏈,則將會被第一次標記並進行一次篩選,篩選條件是此對象是否有必要執行finalize()方法。當對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過,虛擬機將這兩種狀況都是爲「沒有必要執行」。

            2:若是對象有必要執行finalize() 方法,那麼這個對象將會放置在一個叫作F-Quene的隊列中,並在稍後由一個虛擬機會觸發這個方法。

    

    回收方法區

        Java 虛擬機規範中說過能夠不要求虛擬機在方法區實現垃圾收集,並且在方法區中實現垃圾收集的"性價比" 比較低。 在堆中,尤爲是在新生代中,常規應用進行一次垃圾收集通常能夠回收 70%~90%的空間, 而永久帶垃圾收集率遠遠低於此。

        永久帶的垃圾收集主要回收兩部分:廢棄常亮 和 無用的類。

        回收廢棄常亮:以「abc」字符串常亮爲例。「abc」進入了常亮池中,可是當前系統沒有任何一個String對象引用「abc」。則對「abc」進行回收。

        回收無用的類須要進行下面三次判斷:

            1:該類全部的實例都已經被回收。也就是Java 堆中不存在該類的任何實例。

            2:加載該類的 ClassLoader 已經被回收。

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


        

    垃圾回收算法


        標記-清楚算法。

            分爲 "標記" 和 "清除" 兩個階段。

            標記 : 標記處全部須要回收的對象。

            清除 : 標記完成後統一進行回收。

            存在問題:

                效率問題:標記和清除過程的效率都不過高;

                空間問題:標記清除後會產生大量的不連續的內存碎片,空間碎片太多可能會致使之後程序運行過程當中須要分配較大對象時沒法找到足夠的連續內存而不得不提早觸發另外一次垃圾收集動做。


       複製算法。(未解決標記清除法效率問題而產生)

           將內存分爲等大小的兩塊,每次只使用一塊。當這一塊內存用完了,就將還存活的對象複製到另外一塊上面。而後再把已使用過的內存空間一次清理掉。

        

            存在問題:代價是將內存縮小爲原來的一半,代價過高。

            如今的複製算法主要來回收新生代對象。新生代的對象「朝生夕死」,不須要1:1的比例來劃份內存空間,而是將內存分爲一大塊Eden空間和兩塊較小的survicor空間(分配比例 8:1:1)。每當回收的時候將Eden空間和已經使用的Survivor 中的對象存放到另外一塊 Survivor 中,最後清理掉 Eden 和 Survivor 空間。(這樣僅僅只浪費10%)。當Survivor空間不夠用的時候,須要依賴其它內存(老年代)進行分配擔保。


        標記-壓縮算法。

            複製算法在存在對象較多的時候就要進行屢次複製操做,效率會變低。更關鍵的是若是不想進行50%的浪費,就要有空間進行分配擔保,以應對被使用的內存中全部對象100%存活。因此老年代不能採用這種算法。

            標記壓縮:標記過程仍然和 "標記清除" 算法同樣 , 壓縮過程就是讓全部可存活的對象移動到內存的一端,而後清理掉邊界外的對象。

            

       分代收集算法。

          根據對象存活週期不一樣將內存劃分爲新生代、老年代。

            新生代:每次都會有大量的對象死去,只有少許存活。採用複製算法。

            老年代:對象存活率高、沒有額外空間對它進行分配擔保。採用 「標記壓縮算法」

相關文章
相關標籤/搜索