JVM總結_垃圾回收機制

本文是閱讀筆記,本人將書中的要點提取出來方便複習、記憶。如須要詳細瞭解,請翻閱《深刻理解Java虛擬機》(周志明)第三章相關部分,諒本人水平有限,描述中有不正確的地方,還請包含與指正。java

  • 概述

    • 問題算法

      • 哪些對象須要回收?
      • 何時回收?
      • 如何回收?
    • 垃圾回收發生的位置api

      • 方法區

    程序計數器、虛擬機棧、本地方發棧是線程私有的,隨線程生滅。堆和方法區是線程共享的,這些地方的內存都是動態的分配和回收的。多線程

  • 判斷對象須要回收的方法 -- (哪些對象須要回收)

    • 引用計數法( Reference Counting)
      • 描述
        • 當引用失效時,計數器值減1。
        • 當被引用時,計數器值加1。
        • 當計數器爲0時,表示該對象沒有被引用,能夠被回收。
      • 優勢
        • 實現簡單
        • 效率高
      • 缺點
        • 難以解決對象間相互循環利用的問題
    • 可達性分析算法 ( Reachability Analysis)
      • 描述
        • 從GCRoots起始點開始向下搜索,當一個對象到達GCRoots沒有任何引用鏈相連,即從GCRoots到此對象不可達,則該對象不可用。
      • GCRoots對象
        • 虛擬機棧(棧幀中的本地變量表)中引用的對象
        • 方法區中類靜態屬性引用的對象
        • 方法區中常量引用的對象
        • 本地方法棧中JNI(Native方法)引用的對象
  • 對象的引用

    • 強引用(Strong Reference)
      • 相似Object obj = new Object(),只要強引用存在,垃圾回收器永遠不會回收該對象。
    • 軟引用(Soft Reference)
      • 描述一些還有用但非必需的對象。系統將要發生內存溢出以前,將這些對象列入回收範圍,進行第二次回收。
      • 若是第二次回收後依然沒有足夠的內存,將會發生內存溢出異常。
    • 弱引用(Weak Reference)
      • 描述一些非必需的對象。這些對象只能存活在下次垃圾回收以前,不論內存是否充足,下次垃圾回收器都會回收這些對象。
    • 虛引用(Phantom Reference)
      • 幽靈引用或幻影引用。
      • 對象是否存在虛引用,徹底不會對其生存時間影響。
      • 沒法經過虛引用得到對象實例。
      • 設置虛引用,該對象被回收時收到一個系統通知。
  • 對象的死亡 -- (何時回收)

    若是對象通過可達性分析以後沒有與GCRoot相連的引用鏈,若是須要執行finalize()方法的對象,至少須要經歷兩次標記過程。(《深刻理解Java虛擬機》原文:即便在可達性分析算法中不可達的對象,也並 非是「非死不可」的,這個時候他們暫時處於「緩刑」階段,要真正宣告一個對象的死亡,至少須要經歷兩次標記過程。加粗部分我認爲可能會有歧義,因此用了我本身容易理解的說法。)併發

    • 第一次標記:可達性分析以後,而且篩選出須要執行finalize()方法.
          如下狀況屬於不須要執行finalize()方法:異步

      • 當前對象沒有覆蓋finalize()方法
      • finalize()方法被虛擬機調用過
    • 第二次標記:若是該對象須要執行finalize()方法,會被放入F-Queue隊列中,以後系統執行對象的finalize()方法。再執行玩finalize()方法以後,若是該對象從新被引用,將會被移除「即將回收」的集合。不然,必定會被回收。     關於finalize()方法和F-Queue隊列:性能

      • F-Queue中的finalize()方法由虛擬機創建且優先級很低的Finalizer線程執行。
      • 虛擬不會保證該線程執行完成,由於可能對象的finalize()方法執行時間太長,致使內存回收系統崩潰。
      • finalize()方法是對象最後拯救本身,不被回收的機會。
      • finalize()方法只會被系統調用一次。
  • 方法區對象的回收

    • 特色spa

      • 方法區的垃圾手機效率很低
    • 回收內容線程

      • 廢棄常量
      • 無用的類
    • 判斷是不是廢常量server

      • 沒有任何對象引用該常量
    • 判斷是不是無用的類

      須要同時知足一下條件:

      • 該類全部實例都已經被回收。
      • 加載該類的ClassLoader已經被回收。
      • 該類對應的java.lang.class對象沒有在任何地方引用,沒法在任何地方經過反射訪問該類的方法。 知足以上三個條件並非必定會被回收,虛擬機提供了Xnoclassgc進行控制。
  • 垃圾收集算法 -- (如何回收)

  • 標記-清除算法

    • 兩個階段

      • 標記

        標記出全部須要回收的對象

      • 清除

        標記完成後統一回收全部被標記的對象

    • 缺點

      • 效率低
      • 浪費空間。標記清除後會出現大量的不連續的內存碎片,致使沒法分配內存較大的對象從而再一次觸發垃圾回收。
    • 複製算法

      • 過程
        • 將可用內存按容量分爲大小先相同的內存塊,每次只使用期其中的一塊。當這一塊內存用完時,將全部存活的對象都複製到另外一塊,而後清空已使用的內存。
    • 缺點
      • 將內存縮小至原來的一半,代價過高
      • 存活的對象過多時,複製的時間長、效率低。
      • 虛擬機的新生代回收採用複製算法
      • 新生代對象很快就會死亡,因此不須要按照1:1比例劃份內存
      • 虛擬機將內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor空間
      • 回收時,將Eden和其中一塊的Survivor中存活的對象,複製到另外一塊Survivor空間。
      • HotSpot虛擬機默認Eden和Survior的大小爲8:1.
      • 複製到另外一塊Survivor時,若是空間不夠,須要其餘內存(老年代)進行分配擔保。、
  • 標記-整理算法

    • 虛擬機的老年代通常採用標記-整理算法
    • 老年代可能出現對象100%都存活的極端狀況,因此內存必須浪費50%空間。

    • 特色

      • 標記-整理算法的標標記過程與標記-清楚算法相同,但後續不是直接對可回收對象進行整理,而是讓存活的對象都向一端移動,而後直接清除這一端比邊界之外的內存。
  • 分代收集算法

    • 根據對象存活週期的不一樣將內存劃分爲幾塊,java堆通常分爲新生代和老年代,根據年代的特色選取不一樣的垃圾回收算法。
  • 收集算法的選擇

    • 每次垃圾收集都有大量對象死去,少許存活 ==> 複製算法
    • 對象存活率高。沒有額外空間進行分配擔保 ==> 標記-清楚 或 標記-整理算法
  • 垃圾收集器

    垃圾收集器是內存回收逇具體實現。虛擬機廠商通常都會提供參數供用戶根據本身的應用特色和要求和組合出各個年代所使用的收集器。

    • Serial收集器

      • 最基本、歷史最悠久的單線程收集器。Serial收集器在虛擬機Client模式下默認的新生代收集器。
      • 缺點
        • 它在進行垃圾收集時,必須暫停其餘全部工做線程,直到它收集結束。
      • 優勢
        • 簡單、高效(與其餘單線程的收集器相比)
    • ParNew收集器

      • Serial收集器的多線程版本,其與行爲與Serial收集器徹底一致。
      • 它是虛擬機server模式下首選的新生代收集器
      • 它是除了Serial收集器以外惟一能與CMS收集器配合工做的收集器
      • ParNew收集器在單線程的環境下的性能並不比Serial收集器要好。
    • Parallel Scavenge 收集器

      • 使用複製算法新生代多線程收集器,也稱爲「吞吐量優先」收集器。
      • 特色
        • 它的目的是達到一個可控制的吞吐量。 吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)
        • 高吞吐量能夠高效率的使用cpu時間,儘快的完成任務,主要是和在後臺運算而不須要太多交互的任務。
    • 三個參數

      • -XX:MaxGCPauseMills:控制最大垃圾收集停頓時間。容許一個值大於0的毫秒數,收集器儘量保證收集停頓時間不超過該值。
        • GC停頓時間是以吞吐量和新生代空間來換取的,因此並非該值越小越好。
      • -XX:GCTimeRadio:設置吞吐量大小。值爲0-100的整數,表明垃圾收集時間,佔總時間的比率,至關因而吞吐量的倒數。
      • -XX:UseAdapitiveSizePolicy:打開該參數後,GC會採用GC自適應策略,動態調整這些參數以提供合適的停頓時間或者最大的吞吐量。
    • Serial Old 收集器

      • 它是Serial收集器的老年代版本,單線程收集器,使用標記-整理算法。
      • 用途
        • 虛擬機啊Client模式下使用
        • JDK1.5以前與Parallel Scavenge收集器配合使用。
        • 做爲CMS收集器的後備預案。
    • CMS收集器

      • CMS(Concurrent Mark Sweep)收集器是一種一獲取最短回收停頓時間爲目標,提升用戶體驗。
      • 基於標記-清除算法

      • 運做過程

        • 初始標記
          • 標記GCRoots能直接關聯到的對象,速度很快。
        • 併發標記
          • 標記能夠回收的對象
        • 從新標記
          • 修正併發標記時,由於程序繼續運做致使表動的標記記錄。
        • 併發清除
          • 清除被標記能夠回收的對象
      • 優勢
        • 併發收集
        • 低停頓
      • 缺點

        • 對CPU資源很是敏感。

          在併發階段,雖然不會致使用戶線程停頓,可是會佔用一部分CPU資源,致使應用程序變慢,總吞吐量下降

        • 沒法處理浮動垃圾。可能出現「Concurrent Mode Failure」失敗,而致使另外一次Full Gc 的產生。

          浮動垃圾:併發清理階段,用戶線程並無中止,全部依然會有新的垃圾產生,這部分垃圾CMS收集器沒法在當次收集時處理。

          也正是因爲用戶線程沒有中止,因此CMS收集時,虛擬機須要預留一部分空間提供併發收集時的程序運做使用。若是CMS收集期間預留的內存沒法知足程序的須要。就會出現Concurrent Mode Failure失敗,虛擬機就會啓動後備預案:啓用Serial Old 收集器來從新進行老年代的垃圾收集,這就致使了丁頓時間很長。

          • -XX:CMSInitiatingOccupancyFraction :提升觸發CMS收集器的百分比。JDK1.5默認爲68%,JDK1.6默認爲92%。當老年代的使用空間比率達到該值,就會觸發CMS收集器。
          • 基於上一點的描述,參數XX:CMSInitiatingOccupancyFraction若是設置過高,容易致使出現大量的失敗,使性能下降。
        • 會產生大量的空間碎片

          由於CMS收集器i是基於 標記-清除 算法的收集器。因此會差生空間碎片,分配大對象時就會可能由於沒有足夠的連續空間,致使提早觸發一次FullGc

        • 解決辦法

          • 解決對CPU資源很是敏感:

            虛擬機提供紅了「增量式併發收集器」的CMS收集器變種,在併發標記、清理的時候讓GC線程、用戶線程交替執行,儘可能減小GC線程的獨佔資源的時間。可是效果通常,不提倡使用。

          • 解決產生大量的空碎片:

            +XX:+UserCMSCompactAtFullCollection: 開關參數(默認開啓),在須要進行FullGC是,開啓內存碎片和平整理。內存整理沒法異步,因此會使停頓時間變長

            +XX:CMSFullGCBeforeCompaction: 設置多少次不進行碎片整理的FullGC後,跟着來一次進行碎片整理的FullGC.。(0表示每次FullGC都進行碎片整理)

    • G1收集器

      • G1是一款面向服務端應用的垃圾收集器。

      • 特色

        • 並行與併發。
        • 分代收集。

          使用不一樣的方式處理不一樣代的收集。

        • 空間整合。

          G1總體上是基於「標記-整理」算法實現,從局部看,是基於「複製」算法實現。這意味着G1收集器不會產生空間碎片。

        • 可預測的停頓。

          G1除了追求低停頓之外,還能創建可預測的停頓時間模型。G1將內存分爲多個Region,跟蹤每一個Region中的垃圾堆積的價值大小,根據容許的手機實現,優先回收價值最大的Region,保證了 G1收集器在有限的時間內能夠獲取可能高的收集效率。

      • 運做過程

        • 初始標記

          標記GCRoots能直接關聯到的對象,且修改TAMS(Next Top Mark Start)的值,讓用戶程序併發運行時,能在正確的Region中建立新對象。

        • 併發標記

          從GC Root進行可達性分析,找出存活的對象,耗時較長,可是能夠和用戶程序併發執行。

        • 最終標記

          修正在併發標記期間因用戶程序併發運行致使標記產生呢表動的標記記錄。須要暫停線程,可是能夠並行執行。

        • 篩選回收

          首先對各個Region的回收價值和成本進行排序,根據遊湖指望的GC停頓時間來制定回收計劃。

    • 若是應用追求低停頓,G1是個可嘗試的選擇。

    • 若是應用追求高吞吐量,G1並非個很好的選擇。

相關文章
相關標籤/搜索