[Reading] Asking while Reading

           Asking while Reading

          ——讀Java垃圾收集器與內存分配策略java

         Java與C++之間有一堵由內存動態分配和垃圾收集技術所圍成的「高牆」,牆外面的人想進去,牆裏面的人卻想出來。python

爲何要了解垃圾回收?

  當須要排查各類內存溢出、內存泄漏問題的時候,當垃圾回收成爲了系統達到更高並行量的瓶頸的時候,咱們就須要根據狀況(每每是根據硬件和程序算法

以及它們在各類垃圾回收算法下運行的狀況)選擇恰當的垃圾回收方式,做出必要的監控和調節。數組

哪些內存須要回收?

         換一句話來講:哪些內存能夠回收,哪些內存又值得回收;能夠回收明確咱們的執行範圍,而回收價值明確咱們的主要目標,固然這不是必定的,服務器

對每一個項目均可能有不一樣的着眼點,這也是咱們要理解垃圾回收方法與過程的緣由。多線程

Java堆和方法區與棧不一樣,一個接口中的多個實現類須要的內存可能不同,一個方法中的多個分支須要的內存也可能不同,咱們只有在程序處於運性能

行期間時才能知道會建立哪些對象,這部份內存的分配和回收都是動態的,垃圾回收器關注的是這部份內存。spa

           方法區也須要回收嗎?線程

         方法區能夠不回收,由於Java虛擬機規範中說過不要求虛擬機在方法區中實現垃圾收集,更重要的緣由是其性價比通常較低,緣由以下:對象

         咱們知道對方法區的收集集中在對常量、無用的類的收集。

  1)  效率:常規一次垃圾收集能夠回收70%~95%的空間,而根據我以往的經驗,通常狀況下,常量的佔用空間之小和方法的調用區域之普遍讓放置永

    生代的方法區的效率遠低於此。

  2)  條件:雖然斷定一個常量是否廢棄比較簡單,但Java的反射思想讓類的廢棄斷定格外苛刻:

    a)         該類全部的實例都被回收

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

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

    那回收方法區還有意義嗎?

    並不是存在即有意義,固然它也有本身的運用場合:尤爲在WEB或用到控制反轉思惟的應用中大量使用反射、動態生成JSP這類頻繁自定義ClassLoader的

  場景都須要虛擬機具有類卸載的功能,以保證永生代不會溢出。

如何判斷是否能夠回收?

         咱們已然明確了哪些內存能夠回收,可是在一個時間點具體地如何去回收呢?雖然在python中採用了引用計數算法,可是我得很遺憾地告訴你,這

 樣作並不可靠,而且更關鍵地是Java並無採用這一算法。

        在堆裏存放着Java世界裏幾乎全部的對象實例,垃圾收集器在對堆進行回收前,第一件事情就是要肯定這些對象之中哪些還存活着。

         引用計數算法

           方法:給對象添加一個引用計數器,每當有一個地方引用它,計數值加一;當引用失效時,計數值減一。所以,當對象的計數值爲0時就能夠被清除

  掉了。

           缺陷:(致命)若是兩個對象互相引用,而後咱們又丟掉了對這兩個對象的引用,那麼引用計數算法就沒法通知GC收集器回收它們的空間。

       可達性分析算法

           根據圖論中可達性的思想以一些靜態地點(如虛擬機棧、方法區中類靜態屬性、方法區中常量)引用的對象爲起始點,從這些節點開始向下搜索,那

  些不可達的對象就是可回收的對象。

如何回收?

         實際上,這一部分並不會給出最好的算法,在IT行業也是這樣,只有更適合問題的方法,沒有適合全部問題的方案。

         標記清除算法(最基礎)

           過程:      標記:標記全部須要回收的對象。(在前面已經講述)

                              清除:在標記完成後統一回收全部被標記的對象。

           缺點:      1)效率:標記和清除兩個過程的效率都不高

          2)  空間:操做完成後會產生大量內存碎片

  複製算法(解決效率問題)(新生代)

    過程:     將內存分爲三塊,一塊較大的Eden空間和兩塊較小的Survivor空間。當回收時,將Eden和Survivor中存活的對象一次性複製到另外一塊Survivor空間

      裏,最後清理掉Eden和Survivor中的數據。

      優勢:     由於大多數新生代存活時間都比較短,付出少許的內存空間(通常是8:1:1)就能夠達到很好的效果(根據IBM一項調查顯示,新生代的對象98%

      都是「朝生夕死」)。

    缺點:     在Survivor區域不夠時,就要依賴其餘區域(老生代)進行分配擔保,實在不行只得進行全內存的垃圾回收。因此這不適用於一些新生代很大、

      生存週期又較長的狀況。

  標記-整理算法(新生代)

    過程:     標記過程仍和「標記-清除」算法同樣,但以後是讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存。

    優勢:     1)不須要額外的空間閒置不用。(或稱爲分配擔保)

        2)  有效地解決了複製算法中當對象存活週期長的缺點

    缺點:     也正如它所要解決的問題,它不能解決老生代成片地存活週期長的問題,由於正如數組的移動,低效隨之而來。

  分代收集算法

    當前虛擬機都採用「分代收集」的思想,說其爲思想,由於我想其中並無什麼新的內容,只不過是繼承前面的思想,面對不一樣問題選用不一樣的手段罷了。

    在新生代中,只有少許的對象存活,那就採用複製算法,只須要付出少許存活對象的複製成本就能夠完成收集。

    在老生代中,由於對象存活率高、沒有額外空間對它進行擔保,那就必須採用「標記-清理」或者「標記-整理」的算法進行回收。

 

         現在編譯器中採用的垃圾收集器:

                   新生代:Serial、ParNew、Parallel Seavenge

                   老生代:CMS、Parallel Old、Serial Old

                   以及較爲綜合的G1收集器都是創建在以上思想的基礎之上,其中的區別也每每是是否面向多線程?是否着重考慮單次收集時間(增長次數減小

  間斷)仍是着重考慮CPU佔用時間(增長每次間斷時間而減小間斷次數),這也是與客戶體驗和服務器處理性能相關的。咱們須要作的只是針對特定狀況設

  定合適的參數而已了。有了以上的基礎知識,我相信能在處理垃圾收集問題時候會有一個清晰的方向。

相關文章
相關標籤/搜索