1. 程式人生 > 其它 >JVM之垃圾回收

JVM之垃圾回收

物件是否存活: ①.引用計數演算法: 描述:每個物件有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數為0時可以回收. 優勢:簡單高效 缺點:對於迴圈引用的物件無法回收(兩個物件相互引用) ----------------------------------- ②.可達性分析演算法: 描述:GC的時候首先會根據一系列可以被定義為GC Roots的物件作為起始點依次往下搜尋 這個搜尋的路徑即為引用鏈,若物件沒有被引用鏈連線到GC Roots,則將標記為可清除(證明此物件是不可用的。不可達物件。),在下次GC的時候可能會被清除. 在Java語言中,可以被定義為GC Roots的:   虛擬機器棧中引用的物件。   方法區中類靜態屬性實體引用的物件。   方法區中常量引用的物件。   本地方法棧中JNI引用的物件。 優勢:彌補了引用計數演算法的不足;

再談引用:

 無論引用計數演算法還是可達性分析演算法,判斷物件是否存活都與引用有關

 引用分為:強引用,軟引用,弱引用,虛引用,引用強度依次減弱

①.強引用:

        建立一個物件並把這個物件賦給一個引用變數。被強引用的物件永遠不會被垃圾回收 , 即使記憶體不足的時候。

強引用物件普遍存在,例如:A a = new A();

②.軟引用:

        用來描述還有用但並非必須的物件,軟引用的物件當系統記憶體充足時和強引用沒有太多區別,但記憶體不足將要發生記憶體溢位之前會把這些物件列入回收範圍進行第二次回收。若此次回收還沒有足夠的的記憶體會拋記憶體溢位異常.

      通過SoftReference類來實現:

public static void main(String[] args){

  //建立軟引用陣列
  SoftReference<Book> [] books = new SoftReference[100]; 
  //賦值  
  for(int i = 0; i< books.length ;i++){
    books[i] = new SoftReference<Book>(new Book(""+i ,i));
  }
  
    //測試
  System.out.println(books[1].get());
   //通知系統進行回收
  System.gc();
  System.runFinalization();
  
  System.out.println("---------------");
  System.out.println(books[1].get());
  }
//注意:當系統不足時才會回收軟引用的物件。否則不會回收

③. 弱引用:

        用來描述非必須的物件。被弱引用關聯的物件生命週期較短,只能生存到下次垃圾收集發生之前, 無論記憶體是否足夠都會回收

弱引用通過WeakReference類來實現:

 public static void main(String[] args) {
   String str = new String("aaa");
//   String str = "aaa"; 這種建立是在常量池中
   //建立一個如引用物件 指向 str物件
   WeakReference<String> wrStr = new WeakReference<String> (str);
  
   str =null;
   //輸出
   System.out.println(wrStr.get()); //aaa
   //強制垃圾回收
   System.gc();
   System.out.println(wrStr.get()); //null
 }

④.虛引用:

        軟引用和弱引用可以單獨使用,虛引用不能單獨使用,物件是否有虛引用關聯完全不會對其生存時間造成影響。為物件設定虛引用的唯一作用是就跟蹤物件被垃圾回收的狀態(物件被收集器回收會收到一個系統通知)。

垃圾收集演算法: ①.標記清除演算法:         描述:最基礎的回收演算法;演算法分為標記和清除兩部分 ,首先標記出所有需要回收的物件,其次在標記完成後統一回收掉所有被標記的物件. 缺點: ①.標記和清除過程效率低下; ②.會造成記憶體碎片,若記憶體碎片過多可能會導致需要分配較大物件時無法找到足夠的連續記憶體(大的物件放不下.)而不得不提前觸發一次垃圾收集動作。 ---------------- ②.複製演算法:        描述:    複製演算法為了解決效率問題而出現的。它將記憶體分為大小對等的兩部分,每次只使用其中一塊,在GC的時候將還在使用的物件複製到另一塊區域,整體釋放當前區域的記憶體;        優勢:     速度快,實現簡單,執行高效,不會造成記憶體碎片        缺點:     太浪費記憶體,每次都要犧牲50%的記憶體,如果生命週期過長的物件逐漸增多,會導致複製趨於頻繁從而導致效率下降。         Hotspot虛擬機器預設將新生代中的Eden空間,兩小塊survivor空間,按比例為8:1:1分配, 這樣每次GC的時候將Eden,和其中一塊survivor中的存活物件複製到另一塊survivor中,整體釋放,每次只會浪費10%的記憶體,若存活物件超過10%,此時survivor空間將不夠, 因此需要依賴於其他空間進行擔保(老年代)。        新生代採用的就是複製演算法,因為新生代物件代謝更加頻繁,進而導致存活的物件不多,所以需要複製的物件不多,可以提升效率, 而對於老年代則不適用,因為能進入老年代說明該物件生命週期比較長。 ---------------- ③.標記整理演算法:       描述 :   是對標記清除演算法的一個改良,標記過程不變,在GC的時候將需要清除的物件統一壓縮到一邊,不需要回收的壓縮到另一邊,然後整體清除界線一端需要回收的記憶體空間 適用於老年代收集,因為老年代的GC不是很頻繁      優勢 : 不會造成記憶體碎片

---------------------------- 垃圾收集器: 一.新生代收集器:    Serial收集器:     描述: 是最基本,發展歷史最悠久的收集器,它是一個單執行緒收集器,它在進行垃圾回收的時候,必須停止其他所有工作執行緒,直到它收集結束位置.是client端預設的新生代垃圾收集器.    優勢:  簡單高效,因為是一個單執行緒收集器,所以沒有執行緒互動的開銷.    不足:  工作時必須停止其他所有工作執行緒 ParNew收集器:       描述: 是 Serial收集器的多執行緒版本.其餘行為跟Serial收集器相同.是server端首選的新生代垃圾收集器,並且目前只有它可用與CMS收集器(老年代中比較優秀的一個收集器)配合工作.   在設定-XX:+UseConcMarkSweepGC引數後,預設的新生代收集器,也可以使用-XX:+UseParNewGC來強制指定.預設開啟的收集器執行緒數與CPU數量相同,可用使用-XX:ParallelGCThreads引數來限定垃圾收集器的執行緒數.    優勢: 適用於多CPU環境.   不足: 工作時必須停止其他所有工作執行緒 Parallel Scavenge收集器:  描述:  它是一個吞吐量優先的新生代收集器,它的目標是對於吞吐量可控制,吞吐量 = 程式執行時間 / 虛擬機器總執行時間(程式執行的時間 + 垃圾收集的時間).例如虛擬機器總執行100m,垃圾收集耗費1m,吞吐量即為99%.    可以從:①.控制最大收集停頓時間(-XX:MaxGCPauseMillis),單位ms,取值範圍需大於0,收集時間保證儘可能的不超過這個設定值.;GC停頓時間是以犧牲新生代空間和吞吐量換取的,系統把新生代調小雖然收集速度加快了但是也導致垃圾收集更加的頻繁,吞吐量隨之下降; ②.設定吞吐量大小(-XX:GCTimeRatio)範圍0-100之間,預設值99即為允許最大1%的垃圾收集時間來控制吞吐量;兩個引數2選1; 引數 -XX:UseAdaptiveSizePolicy,當開啟之後就不需要手工指定新生代大小,Eden與Survivor的比例(-XX:SurvivorRatio),晉升老年代物件的大小(-XX:PretenureSizeThreshold)等引數,虛擬機器會根據系統的執行情況收集效能監控資訊,動態調整這些引數,以便提供最適合的停頓時間或最大吞吐量,這種調節方式稱為GC自適應調節策略。 優勢:  可控的最大停頓時間(停頓時間越短,程式響應速度越快,能夠提升使用者體驗)和吞吐量以及自適應調節策略。 不足:  減小最大收集停頓時間會導致收集頻率增加,吞吐量下降,反之提高吞吐量則可能會導致停頓時間變長,響應速度降低, 收集期間使用者執行緒暫停。 二,老年代收集器: Serial Old收集器:        描述: 它是Serial收集器的老年代版本,跟Serial一樣是一個單執行緒收集器,使用"標記整理演算法"。用於Client模式下的虛擬機器使用;在Server模式下作為CMS收集器的後備預案。JDK1.5及以前配合Parallel Scavenge收集器使用。       優勢:不會造成記憶體碎片       不足: 同樣收集階段暫停其他所有使用者執行緒。 Parallel Old收集器:         描述:  作為Parallel Scavenge收集器的老年代版本,同樣是一個多執行緒收集器,使用"標記整理演算法"。JDK1.6才開始提供與Parallel Scavenge收集器組合使用。         優勢: 同樣注重吞吐量.不會造成記憶體碎片         不足: 收集期間使用者執行緒暫停 CMS收集器:        描述:  它是一個以減少垃圾收集停頓時間為目標的收集器。由於執行階段使用者執行緒依然執行,所以需要預留足夠的空間給使用者執行緒使用。JDK1.5當老年代空間使用達到68%後會觸發一下GC,JDK1.6中啟動閥值調到92%, 若CMS執行期間預留的空間不足以滿足程式使用就會出現一次"Concurrent Mode Failure"失敗, 這時候會啟動後備預案即臨時啟動Serial Old收集器重新進行老年代的垃圾收集,停頓時間會很長(Serial Old執行,使用者執行緒暫停),因此-XX:CMSInitiatingOccupancyFraction設定過高的話容易導致大量"Concurrent Mode Failure"失敗,效能反而下降。 CMS採用的是"標記清除演算法",執行過程分為4個階段:     ①.初始標記:標記GC Roots能直接關聯的物件,此階段速度快,; 需要暫停使用者執行緒。     ②.併發標記:同時開啟GC和使用者執行緒,用一個閉包結構去記錄可達物件。但在這個階段結束,這個閉包結構並不能保證包含當前所有的可達物件。因為使用者執行緒可能會不斷的更新引用域,所以GC執行緒無法保證可達性分析的實時性。所以這個演算法裡會跟蹤記錄這些發生引用更新的地方。     ③.重新標記:修正併發標記階段因使用者執行緒繼續執行而導致標記狀態產生變化的那一部分標記記錄,停頓時間比初始標記階段稍長,遠比並發標記階段時間短; 需要暫停使用者執行緒。     ④.併發清除:開啟使用者執行緒,同時GC執行緒開始對為標記的區域做清掃。 優勢: 併發收集,低停頓 不足:  <1>.對CPU資源敏感(佔用一部分執行緒會導致吞吐量下降);<2>.會造成記憶體碎片(碎片太多,在分配大的物件時會導致因為無法找到足夠大的連續的空間從而不得不提前觸發一次Full GC;  可以通過設定-XX:+UseCMSCompactAtFullCollection引數在CMS頂不住要進行Full GC時進行記憶體碎片整合,預設開啟, 整理的階段是不能併發執行的,雖然解決了記憶體碎片的問題,但會帶來GC停頓時間變長的問題,可以通過-XX:CMSFullGCsBeforeCompaction引數設定執行多少次不壓縮的Full GC後進行一次壓縮GC,預設為0,表示每次Full GC都要進行碎片整理); <3>.無法清除"浮動垃圾"(因為清除階段,使用者執行緒依然在執行所以就會產生新的垃圾,這些垃圾出現在標記階段之後,只有下一次GC才會被清理,這類垃圾被稱為浮動垃圾)。 G1收集器:         描述:  同樣是一個以降低收集停頓時間為目的的收集器, G1 GC是Jdk7的新特性之一、Jdk7+版本都可以自主配置G1作為JVM GC選項;作為JVM GC演算法的一次重大升級、JDK7u後G1已相對穩定、且未來計劃替代CMS、採用"標記整理演算法"。G1將記憶體化整為零,把堆空間劃分成了互相獨立的區塊。且每類區域空間可以是不連續的,當併發後臺執行緒尋找可回收的物件時、有些區塊包含可回收的物件要比其他區塊多很多。雖然在清理這些區塊時G1仍然需要暫停應用執行緒、但可以用相對較少的時間優先回收包含垃圾最多區塊。這也是為什麼G1命名為Garbage First的原因:第一時間處理垃圾最多的區塊。         優勢:  <1>.不會造成記憶體碎片;                 <2>.低停頓;                 <3>.不需要與其他收集器配合使用。                 <4>.Eden, Survivor, Old區不再固定、在記憶體使用效率上來說更靈活                 <5>.G1在回收記憶體後會馬上同時做合併空閒記憶體的工作

---------------------

常用引數:

 UseSerialGC

 虛擬機器執行在Client 模式下的預設值,開啟此開關後,使用Serial +
Serial Old 的收集器組合進行記憶體回收

 UseParNewGC

 開啟此開關後,使用ParNew + Serial Old 的收集器組合進行記憶體回收

 UseConcMarkSweepGC

 開啟此開關後,使用ParNew + CMS + Serial Old 的收集器組合進行記憶體
回收。Serial Old 收集器將作為CMS 收集器出現Concurrent Mode Failure失敗後的後備收集器使用

 UseParallelGC

 虛擬機器執行在Server 模式下的預設值,開啟此開關後,使用Parallel
Scavenge + Serial Old(PS MarkSweep)的收集器組合進行記憶體回收

 UseParallelOldGC

 開啟此開關後,使用Parallel Scavenge + Parallel Old 的收集器組合進行記憶體回收

 SurvivorRatio

 新生代中Eden 區域與Survivor 區域的容量比值, 預設為8, 代表
Eden :Survivor=8∶1

 PretenureSizeThreshold

 直接晉升到老年代的物件大小,設定這個引數後,大於這個引數的物件
將直接在老年代分配

 MaxTenuringThreshold

 晉升到老年代的物件年齡。每個物件在堅持過一次Minor GC 之後,年
齡就加1,當超過這個引數值時就進入老年代

 UseAdaptiveSizePolicy

 動態調整Java 堆中各個區域的大小以及進入老年代的年齡

 HandlePromotionFailure

 是否允許分配擔保失敗,即老年代的剩餘空間不足以應付新生代的整個
Eden 和Survivor 區的所有物件都存活的極端情況

 ParallelGCThreads

 設定並行GC 時進行記憶體回收的執行緒數

 GCTimeRatio

 GC 時間佔總時間的比率,預設值為99,即允許1% 的GC 時間。僅在
使用Parallel Scavenge 收集器時生效

 MaxGCPauseMillis

 設定GC 的最大停頓時間。僅在使用Parallel Scavenge 收集器時生效

 CMSInitiatingOccupancyFraction

 設定CMS 收集器在老年代空間被使用多少後觸發垃圾收集。預設值為
68%,僅在使用CMS 收集器時生效

 UseCMSCompactAtFullCollection

 設定CMS 收集器在完成垃圾收集後是否要進行一次記憶體碎片整理。僅
在使用CMS 收集器時生效

 CMSFullGCsBeforeCompaction

 設定CMS 收集器在進行若干次垃圾收集後再啟動一次記憶體碎片整理。
僅在使用CMS 收集器時生效