1. 程式人生 > >jvm垃圾收集器理解

jvm垃圾收集器理解

  • 1、java執行時區域

    執行時區域分為 兩類:執行緒私有和執行緒共享。

    • 執行緒私有

      程式計數器:
              當前執行緒所執行的行號指令器。Java虛擬機器的多執行緒是通過執行緒輪流切換並配置處理執行時間的方式來實現的,在同一個時刻,cpu只能執行一個執行緒,為了執行緒切換後能恢復到正確的
              執行位置,每個執行緒都需要有一個獨立的程式計數器。
              若正在執行的是java方法,則計數器記錄的是正在執行的位元組碼指令地址
              若正在執行的是native方法,則計數器為0
      java虛擬機器棧
              java虛擬機器也是私有的,它的生命週期與執行緒相同,虛擬機器棧描述的是java方法執行的記憶體模型,每個方法執行的同時都會建立一個棧幀用於存放區域性變量表、運算元棧,動態連結,方法出口等資訊。
              區域性變量表存放了編譯期可知的基本資料型別,引用資料型別,和returnAddress型別(指向一條位元組碼指令地址),區域性變量表的記憶體空間在編譯器確定,在執行期不變。
              可導致兩種異常:執行緒請求的棧深度大於虛擬機器允許的深度-StarkOverflowError。虛擬機器無法申請到足夠的記憶體---OutOfMemoryError  
      本地方法棧
              和虛擬機器棧類似,本地方法棧為虛擬機器使用的native
      方法服務。本地方法也會丟擲StackOverflowError 和OutOfMemoryError異常
    • 執行緒共享

      java堆
              java堆是所有執行緒共享的一塊區域。在虛擬機器啟動時建立,主要是存放物件例項和陣列。
              堆是垃圾回收器主要管理的區域:主要分為新生代和老年代
              從記憶體分配角度看,堆可劃分出多個執行緒私有的分配緩衝區
              可以通過-Xmx  -Xms 分配堆記憶體
      方法區
              主要用於存放已經被虛擬機器載入的類資訊、常量、靜態變數、即使編譯器編譯後的程式碼等資料
              Gc主要回收常量池和型別的解除安裝。
              執行時常量池是方法區的一部分
      執行時常量池
              Class檔案的常量池用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類載入後存放在執行時常量池中
              還把翻譯出來的直接引用也放在執行時常量池中,執行時產生的常量也放在裡面
      
  • 2、垃圾回收機制

    首先判斷物件是否活著

    引用計數法
            給物件設定引用計數器,每引用該物件一次,計數器就加1,引用失效時,計數器就減1,當計數器為0時,則該物件就回收
            java並沒有用到引用計數器,很難解決迴圈引用。
    
    可達性分析演算法
            通過GC Roots物件作為起始點,從這些節點開始鄉下搜尋,搜尋所走過的路徑稱為引用鏈,當一個物件到GC Roots沒有任何引用鏈相連時,證明此物件不可用。
            作為GC Roots物件包括:
                虛擬機器棧(棧幀中的本地變量表)中引用的物件。
                方法區中靜態屬性引用的物件
                方法區中常量引用的物件
                本地方法棧中JNI(native方法)引用的物件
    

    在可達性分析過程中,物件引用型別會對物件的生命週期產生影響

    java幾種型別的引用
        強引用:只要強引用還存在,垃圾收集器永遠不會回收掉被引用的物件。
        軟引用:在系統將要發生記憶體溢位之前,將會把這些物件列進回收範圍進行二次回收。SoftReference類來實現引用
        弱引用:非必須物件,只能生存到下一次GC收集之前,當垃圾收集工作時,無論當前記憶體是否足夠,都會回收掉弱引用關聯的物件。WeakReference類來實現引用
        虛引用:無法通過虛引用獲得物件例項,也不會對物件的生存時間產生影響、唯一目的:當該物件被Gc收集時,收到一個系統通知。用PhantomReference類實現
    
    一個物件死亡,至少要經歷兩次標記過程  
        首先進行可達性分析,找出沒有與GC Roots相連線的引用鏈,第一次標記。
        標記後進行篩選,篩選的條件是此物件是否有必要執行finalize()方法,當物件沒有覆蓋finalize()方法,或者finalize()已經被呼叫過了,就視為沒有必要執行。
        若有必要執行,會放置在一個叫做F-Queue佇列中,由jvm自動建立的低優先順序的Finalizer執行緒去執行它。(但不會承諾會等待它執行結束)
        finalize()方法是物件最後一次自救機會,只要重新與引用鏈上的任何一個物件建立關聯即可,則它會被移出要回收的物件的集合。其他物件則會被第二次標記,進行回收。
    
    回收方法區
        永久代中主要回收兩部分內容:廢棄常量和無用的類
        廢棄常量回收
            舉例:字串 a 進入常量池,沒有任何物件引用這個常量,如果發生記憶體回收,a常量就會被系統清理出常量池
        無用的類需滿足3個條件
            該類的所有例項物件已被回收
            載入該類的ClassLoader已被回收
            該類的Class物件沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法
    
  • JAVA中的垃圾回收演算法有

    標記-清除演算法(Mark-Sweep)
        兩個階段:標記、清除
        標記階段:首先通過根節點,標記所有從根節點開始的可達物件。未被標記的物件就是未被引用的垃圾物件。
        清除階段:清除所有未被標記的物件。
        缺點:兩個階段效率都不高,容易產生大量記憶體碎片。
    複製演算法(Copying)
        把記憶體分為大小相同的兩塊,當一塊的記憶體用完了,就把可用的物件複製到另一塊上,將使用過的一塊一次性都清除掉。
        缺點:浪費了一般記憶體
    標記-整理
        適用於存活物件較多的場合。如老年代。
        首先需要從根節點開始,對所有可達的物件做一次標記,將所有存活的物件壓縮到記憶體的一端,之後,清理邊界外所有的空間。
    分代收集
        依據物件的存活物件進行分類,短命物件為新生代,長命物件為老年代。
        少量物件存活,採用複製演算法
        大量物件存活,採用標記清除或標記整理
    
  • GC記憶體分配規則

    堆分為新生代和老年代
        將新生代記憶體分為一塊大的Eden區和兩塊小的Survivor,每次使用Eden和一個Survivor,回收時將EdenSurvivor存活的物件複製到另外一個Survivor(HotSpot的必烈為
Eden:Survivor:Survivor=8:1:1)
    物件優先在Eden區分配
        當Eden區沒有足夠的空間就會發起一次Minor GC
    大物件直接進入老年代
        典型的大物件是很長的字串和陣列
    長期存活的物件將進入老年代
        每個物件有年齡計數器,每經過一次GC,計數器值加一,當到達一定程度時(預設15),就會進入老年代年齡的閾值可通過引數 -XX:MaxTenuringThreshold設定
    動態物件年齡判斷
        Survivor空間中相同年齡所有物件大小的總和大於Survivor空間的一半,年齡大於等於該年齡物件就可以直接進入老年代,無須等到MaxTenuringThreshold要求的年齡
    空間分配擔保
        發生Minor GC前,jvm會檢查老年代最大可用的連續空間是否大於新生代所有物件總空間,若大於,則Minor GC是安全的
        若不大於,jvm會檢視HandlePromotionFailure是否允許擔保失敗,若不允許,則改為一次Full GC
        若允許擔保失敗,則檢查老年代最大可用的連續空間是否大於歷次晉升到老年代物件的平均大小,若大於,則嘗試進行Minor GC;若小於,則要改為Full GC

  • 垃圾收集器
serial(序列收集器)
    特點:單執行緒  採用複製演算法  Stop The World(進行垃圾收集時,必須暫停其他所有的工作執行緒)
    應用:jvm在client模式下的預設新生代收集器
    優點:簡單高效 (單執行緒收集器)

ParNew
    特點:Serial的多執行緒版本,採用複製演算法
    應用:執行在Server模式下的虛擬機器首選的新生代收集器。與CMS配合工作
    優點:在多CPU下,優勢高於serial

Parallel Scavenge
    特點:並行多執行緒收集器,採用複製演算法。吞吐量優先,自適應調節策略
    應用:吞吐量大的時候

Serial Old      
    特點: Servial收集器的老年代版本,單執行緒收集器,採用標記-整理演算法
    應用:Client模式下使用。

Parallel Old
    特點:是Parallel Scavenge收集器的老年代版本。使用多執行緒和標記-整理演算法
    應用:注重高吞吐場合,優先考慮Parallel Scavenge加Parallel Old收集器

CMS(Concurrent Mark Sweep)
     運作過程:
        初始標記:Stop The World,標記GC Roots能直接關聯到的物件。
        併發標記:進行GC Roots Tracing(追蹤)過程。
        重新標記:Stop The World,修正併發標記期間因使用者程式繼續運作而導致標記產生變動的那一部分物件的標記記錄。
        併發清除:清除物件。
     特點:併發收集,低停頓,使用標記-清除演算法 對CPU資源敏感
     缺點:無法處理浮動垃圾(併發清除 時,使用者執行緒仍在執行,此時產生的垃圾為浮動垃圾)。產生大量空間碎片
     -XX:UseCMSCompactAtFullCollection: 用於頂不住進行Full GC時開啟記憶體碎片合併整理過程。
     -XX:CMSFullGCsBeforeCompaction:用於設定執行多少次不壓縮的Full GC後,跟著來一次帶壓縮的。

GC(Garbage-First)
     運作過程:  
        初始標記:stop the world 標記GC Roots能直接關聯到的物件。並且修改NTMS(next top make start)的值,讓下階段使用者程式併發執行時,能在正確可用的Region中建立物件。
        併發標記:可達性分析,找出存活的物件。
        最終標記:修正在併發標記期間因使用者程式繼續運作而導致標記產生變動的那一部分標記記錄
        篩選回收:篩選回收階段首先對各個Region的回收價值和成本進行排序,根據使用者所期望的GC停頓時間來制定回收計劃
    特點: 
        並行與併發:使用多個cpu縮短Stop The World停頓時間,部分其他收集器原本需要停頓java執行緒執行的GC操作,G1收集器仍然可以通過併發的方式讓java程式繼續執行。
        分代收集
        空間整合:從整體看是基於“標記-整理”的,從區域性(兩個region之間)看是基於“複製”的。
        可預測的停頓:使用者可明確指定在一個長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒。
        面向服務端應用,將整個堆劃分為大小相同的region