Java虛擬機(二):垃圾收集器

      程序計數器、虛擬機棧、本地方法棧3個區域隨線程而生,隨線程而滅,在這幾個區域內就不需要過多考慮垃圾回收的問題,因爲方法結束或者線程結束時,內存自然就跟隨着回收了。 棧中的棧幀隨着方法的進入和退出而有條不紊的執行這出棧和入棧操作
      因此虛擬機主要關注java堆和方法區的內存分配和回收。
1、對象存活判定
     1)引用計數算法
        給對象添加一個引用計數器,每當由一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器爲0的對象就是不可能再被使用的。
     缺點:很難解決對象相互循環引用的問題(對象相互循環引用,但其實他們都已經沒有用了)。
    2)可達性分析算法
      
通過一系列的成爲“GC Roots”的對象作爲起始點,從這些節點開始向下搜索,搜索所走過的路徑成爲引用鏈,當一個對象到GC ROOTS沒有任何引用鏈相連時,則證明此對象時不可用的。
     Java語言中GC Roots的對象包括下面幾種:
    -         虛擬機棧(棧幀中的本地變量表)中引用的對象
    -         方法區中類靜態屬性引用的對象
    -         方法區中常量引用的對象
    -         本地方法棧JNI(Native方法)引用的對象



2、垃圾收集算法
 
   1)標記-清除算法
       算法分爲標記和清除兩個階段:首先標記出所有需要回收的對象,在標記完成後統一回收所有被標記的對象。

       不足:一個是效率問題,標記和清除兩個過程的效率都不高;另一個是空間問題,標記清楚之後會產生大量不連續的內存碎片,空間碎片太多可能會導致以後再程序運行過程中需要分配較大的對象時,無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。


   2)複製算法
      將可用內存按照容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這塊的內存用完了,就將還存活着的對象複製到另外一塊上面,然後再把已使用過的內存空間一次清理掉。這樣使得每次都是對整個半區進行內存回收,內存分配時也就不用考慮內存碎片等複雜情況,只要移動堆頂指針,按順序分配內存即可。

     不足:將內存縮小爲了原來的一半。

      實際中我們並不需要按照1:1比例來劃分內存空間,而是將內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor

當另一個Survivor空間沒有足夠空間存放上一次新生代收集下來的存活對象時,這些對象將直接通過分配擔保機制進入老年代。


   3)標記-整理算法 
       類似於標記-清除,但後續步驟不是直接對可回收對象進行清理,而是讓所有存活對象都向一端移動,然後直接清理掉端邊界以外的內存。

3、Java虛擬機垃圾收集器
   
      1)Java內存分帶劃分
       
   Java虛擬機根據對象存活的生命週期長短,將java堆劃分爲新生代、老年代。而方法區主要是持久代,也參與垃圾回收。
參數設置:     
   -Xms128M  :設置堆最最小128M
   -Xmx512M  :設置堆最大512M
   -Xmn20M :設置新生代20M
   -XX:SurvivorRatio=8 :設置Eden區和Survivor區的大小(8:1),分別爲16M、2M
   -XX:PermSize=64M:設置持久代大小64M
   -XX:MaxPermSize=128M:設置持久代最大128M
2)垃圾收集器
   

       可以看到,新生代收集器有Serial、ParNew、Parallel Scanenge收集器,老年代收集器有CMC、Serial Old、Parallel Old收集器。G1收集器是一個全帶收集器,代表當前收集器技術發展的前沿成果。

  • Serial收集器:     

       採用複製算法 ,新生代收集器。      

       這個收集器是一個單線程的收集器,但它的單線程的意義不僅僅說明它會只使用一個CPU或一條收集線程去完成垃圾收集工作,更重要的是它在進行垃圾收集時,必須暫停其他所有的工作線程,直到它收集結束。

  • ParNew 收集器:

       採用複製算法 ,新生代收集器。

        ParNew收集器是Serial收集器的多線程版本,除了使用了多線程進行收集之外,其餘行爲和Serial收集器一樣。

        並行:指多條垃圾收集線程並行工作,但此時用戶線程仍然處於等待狀態。

        併發:指用戶線程與垃圾收集線程同時執行(不一定是並行的,可能會交替執行),用戶程序在繼續執行,而垃圾收集程序運行於另一個CPU上。

  • Parallel Scavenge  收集器:

       採用複製算法 ,新生代收集器。

       Parallel Scavenge收集器是一個新生代收集器,它是使用複製算法的收集器,又是並行的多線程收集器。該收集器的目標是達到一個可控制的吞吐量,

 非其他收集器的儘可能縮短垃圾收集時用戶線程的等待時間。

       吞吐量:就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值。即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)。

       參數 -XX:MaxGCPauseMillis參數用於控制最大垃圾收集停頓時間。
       參數 -XX:GCTimeRatio直接設置吞吐量大小(垃圾收集時間比率,吞吐量的倒數)。

  • Serial Old 收集器:

      採用標記整理的算法,老年代收集器。

      Serial Old 是Serial收集器的老年代版本,是一個單線程收集器。

  •  Parallel Old 收集器:

       採用標記整理的算法,老年代收集器。

       Parallel Old收集器是Paraller Scavenge收集器的老年代版本,使用多線程和標記整理算法。

  •  CMS收集器: 

      採用標記清除算法,老年代收集器。

       CMS收集器是基於標記清除算法實現的,整個過程分爲4個步驟:

        1.初始標記2.併發標記3.重新標記4.併發清除。其中,初始標記和重新標記這兩步驟仍然需要“Stop the world”。

          優點:併發收集、低停頓。

          缺點:1.CMS收集器對CPU資源非常敏感,CMS默認啓動的回收線程數是(CPU數量+3)/4。

                  2.CMS收集器無法處理浮動垃圾,可能出現Failure失敗而導致一次Full Gc地產生,得搭配Serial Old使用。

                   3.CMS是基於標記清除算法實現的,存在碎片問題。

(3)收集器搭配使用及參數設置
        在當前jdk中,主要分爲server模式和client模式,默認是client模式,而在不同的模式中默認的垃圾收集算法也是不一樣的。在client模式中,新生代和老年代默認是Serial和Serial Old,在server模式中默認的新生代和老年代收集器是Parallel Scavenge+Serial Old。java虛擬機的-XX:+PrintGCDetails參數可以打印垃圾收集器的日誌信息。
  •     使用串行收集器
     參數:  -XX:+UseSerialGC 
     說明:虛擬機運行在Client模式的默認值,打開此開關參數後,使用Serial+Serial Old收集器組合進行垃圾收集。
  •    使用ParNew並行收集器
     參數:-XX:+UseParNewGC
     說明:打開此開關參數後,使用ParNew+Serial Old收集器組合進行垃圾收集。
  •    使用並行收集器
     參數:-XX:+UseParallelGC 
             -XX:+UseAdaptiveSizePolicy  :java虛擬機動態自適應策略,動態調整年老代對象年齡和各個區域大小。
             -XX: ParallelGCThreads:設置並行GC時進行內存回收的線程數。
     說明:打開此開關參後,使用Parallel Scavenge+Serial Old收集器組合進行垃圾收集
  •    使用並行Old收集器
     參數:-XX:+UseParallelOldGC 
             -XX:+UseAdaptiveSizePolicy  :java虛擬機動態自適應策略,動態調整年老代對象年齡和各個區域大小。             
             -XX: ParallelGCThreads:設置並行GC時進行內存回收的線程數。
     說明:打開此開關參數後,使用Parallel Scavenge+Parallel Old收集器組合進行垃圾收集    
  •    使用併發收集器
     參數:-XX:+UseConcMarkSweepGC 
            -XX:+CMSInitiatingOccupancyFraction   默認68%,控制當老年代空間使用率超過該值,啓動垃圾收集。
            -XX:+UseCMSCompactAtFullCollection   默認開啓,設置CMS收集器在完成垃圾收集之後是否進行一次內存整理。
            -XX:+CMSFullGCsBeforeCompaction 默認值0,設置CMS收集器在進行多少次垃圾收集不壓縮整理之後才進行一次內存整理。
     說明:打開此開關參數後,使用ParNew+CMS+Serial Old收集器組合進行垃圾收集。Serial Old作爲CMS收集器出現Concurrent Mode Failure的備用垃圾收集器。



參考資料 《深入理解Java虛擬機》

發佈了44 篇原創文章 · 獲贊 5 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章