垃圾收集器與內存分配策略

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

1、判斷對象是否已經死了算法

  1.應用計數算法安全

  給每一個對象添加一個引用計數器,每當有一個地方引用它時,計數器的值就加1,當應用失效的時候就減1,任什麼時候刻計數器爲0的時候表示對象就是不可用狀態。多線程

  可是Java虛擬機並無選擇這種算法,緣由是很難解決對象之間相互引用的問題。併發

  2.可達性分析算法性能

  經過一系列的稱爲「GC Root」的對象做爲起點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Root沒有任何引用鏈相鏈接的時候,證實這個對象是不可用的。spa

  

  在Java語言中,做爲GC Root的對象有以下線程

  ①虛擬機棧中引用的對象。3d

  ②方法區中類靜態屬性引用的對象。指針

  ③方法區中常量引用的對象。

  ④本地方法棧中JNI(Native方法)應用的對象。

 

2、垃圾收集算法

  1.標記—清除算法

  「標記」:首先標記出全部須要回收的對象。

  「清除」:標記完成後統一回收被標記的對象。

  不足之處:① 效率問題,標記和清理兩個過程效率都不高。②標記清理以後會產生大量不連續的內存碎片。

  2.複製算法

  將內存分紅兩個大小相等的兩塊,每次只使用一塊,當這一塊內存用完了,就將還存活的對象複製到另外一塊上,而後把已經使用過的內存空間一次清理掉。

  好處:回收是一次性回收,也不用考慮內存碎片等複雜狀況。只須要移動堆頂的指針,實現簡單,效率高。

  壞處:浪費通常內存。

  如今商業虛擬機使用的這種算法回收新生代:將內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor。當回收的時候,將Eden區和Survivor區中還存活的對象一次性的複製到另外一塊survivor空間上,最後清理Eden區和剛纔使用的survivor區。HotSpot默認Eden與Survivor比例是8:1.,因此只有10%空間浪費。當Survivor空間不夠用時,須要依賴其餘內存(老年代)進行分配擔當

  3.標記—整理算法

  複製算法在對象存活率比較高的時候會進行比較多的複製操做,效率會變低,因此老年代不適合這種算法。

  

 

  4.分帶收集算法

  根據對象活躍週期不一樣將內存劃分爲幾塊:通常把Java堆分爲新生代和老生代。

  新生代:通常使用複製算法。

  老生代:通常使用「標記—清理算法」或者「標記—整理算法」。

 

3、HotSpot的算法實現

  1. 枚舉根節點

  當進行垃圾回收前的對象可達性分析時,對執行時間的敏感體如今GC停頓上。由於這項分析工做必須在一個能保證一致性的快照中進行——整個分析過程當中全部執行都停在某個時間點上。GC停頓是性能瓶頸的重要緣由。

  使用OoMap實現快速節點枚舉。

  2.安全點(Safepoint)

  並非每一個指令都會建立OoMap,咱們會爲特定的指令或者說程序特定的位置建立OoMap,例如:方法調用、循環跳轉、異常跳轉等。

  如何將全部線程都跑到安全點停頓:①搶先式中斷,在GC發生時,首先把全部線程所有中斷,若是發現不在安全點上則恢復線程叫它跑到安全點再中止。

  3.安全區域

  安全區域表示一段代碼,不會引發引用變化。

 

4、垃圾收集器

        

 

  1.Serial收集器

  用戶新生代,使用複製算法。是一個單線程的收集器。它在垃圾回收的時候必須暫停其餘全部的工做線程,直到它收集結束。

  2.ParNew收集器

  就是Serial的多線程版本。它默認開啓的收集線程數與CPU的數量相同。

  3.Parallel Scavenge收集器

  新生代收集器。也是使用複製算法,也是並行多線程收集器。

  特色:其餘收集器關注點是儘量的縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge 目標是達到一個可控制的吞吐量(CPU用戶運行用戶代碼的時間與CPU總時間的比值)。停頓時間越短就會越適合與用戶交互的程序,良好的相應速度能提升用戶體驗,而高吞吐量可以提升CPU的效率,儘快完成任務。

  減小等待時間須要減小新生代的內存,GC頻率也會增長。

  4.Serial Old 收集器

  是Serial  的老年代版本。單線程收集器。CMS收集器的備用方案。使用「標記—整理算法」。

  5.Parallel Old收集器

  Parallel Scavenge收集器的老年代版本。使用「標記—整理算法」。

  6.CMS收集器(Concurrent Mark Sweep)

  目標:獲取最短回收停頓時間的收集器。

  應用:老年代

  算法:「標記—清除」算法。

  步驟:初始標記,併發標記,從新標記,併發清除。

    初始標記:只是標記一下GC Root能直接關聯的對象,速度快,須要GC 停頓。

    併發標記:進行GC Root Tracing 過程。

    從新標記:修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象標記記錄。須要GC停頓。

    併發清除:清除標記的「死亡對象」。

  缺點:

    ①CMS收集器對CPU資源很是敏感

    併發階段會和程序的執行併發使用CPU,CPU資源的利用率就會減小。

    ②沒法處理浮動垃圾

    CMS併發清理階段,用戶線程還在執行,期間還有有垃圾不斷生成,這部分垃圾在標記以後,因此只能推遲到下次清理。因此須要預留一部分空間提供併發收集時程序運做,若是CMS運行期間預留的空間不足時就會出現「Concurrent Mode Failure」失敗,這是虛擬機會啓動備用方案:臨時啓用Serial Old 進行老年代的垃圾回收,這事停頓時間會變長。

    ③CMS使用的是「標記—清理」算法,會出現大量的空間碎片。

  8.理解GC日誌

    

  ①最前面的數字「33.125「和「100.667」表示GC發生的時間,從Java虛擬機啓動以來通過的秒數。

  ②「[GC」 和「[Full GC」此次垃圾收集停頓類型,不是區分新生代和老年代。若是有「Full」說明發生了Stop-The-World

  ③「[DefNew」、「[Tenured」、「[Perm」表示GC發生的區域(新生代、老年代、永久代)

  ④「3324K—>152K(3712K) 0.0025925 」 GC前該區域已使用的容量—>GC後該區域已經使用的容量(該區域的總容量) GC所佔用的時間s。

  ⑤「3324K—>152K(11904K)」表示「GC前Java堆已使用的容量—>GC後Java堆使用的容量(Java堆總容量)」

  

5、內存分配與回收策略

  Java自動內存管理分爲:給對象分配空間和回收分配的對象空間。

  分配空間:對象主要分配在Eden區,若是啓用本地線程分配緩衝將按照線程優先在TLAB上分配。少數狀況直接分配到老年代,取決於哪種垃圾收集器組合還有虛擬機參數相關配置。

     

  1.對象優先在Eden分配

  大多數狀況下,對象在新生代Eden區分配,當Eden區沒有足夠空間進行分配時,虛擬機會發起一次Minor GC 

  -XX:PrintGCDetails 參數打印GC日誌。

  新生代GC(Minor GC):發生在新生代的垃圾回收動做,由於Java對象大多具有朝生夕滅的特性,因此Minor GC會比較頻繁,回收速度也很快。

  老年代GC(Full GC 或者 Major GC):發生在老年代,通常發生時會伴隨一次Minor GC。速度會比較慢。

  2.大對象直接進入老年代

  -XX:PretenureSizeThreshold參數配置,大於這個設置值,直接進入老年代。

  目的:避免在Eden區及兩個Survivor區之間發生大量的內存複製。

  3.長期存活的對象將進入老年代  

  虛擬機給每一個對象定義了一個對象年齡(Age)計數器。若是對象在Eden出生並通過一次Minor GC後仍然存活,而且被Survivor收納,將被複制到Survivor區年齡增長1歲,當年齡增到15歲時就會晉升到老年代。

  -XX:MaxTenuringThreshold配置默認年齡。

  4.動態對象年齡斷定

  並非全部對象必須達到MaxTenuringThreshold值。若是在Survivor空間中相同年齡全部對象大小總和大於Survivor空間的一半,全部大於等於該年齡的對象能夠直接進入老年代。

  5.空間分配擔保

  在發生Minor GC以前,虛擬機會先檢查老年代最大可用的連續空間是否大於新生代全部對象的總和,若是條件成立那麼Minor GC能夠確保是安全的。

  若是不成立,查看HandlePromotionFailure設置值是否容許擔保失敗。若是容許,那麼會繼續檢查老年代最大可用連續空間是否大於歷次晉級到老年代對象的平均大小。若是大於,將嘗試進行一次Minor GC,儘管是冒險的。若是小於或者HandlePromotionFailure設置不冒險,則須要進行一次Full GC。

  -XX:-HandlePromotionFailure

  若是出現HandlePromotionFailure失敗,那隻好在失敗後從新發起一次Full GC。雖然失敗時會繞圈子,可是建議開啓避免頻繁Full GC。

 

 

  筆者的話:

  深刻理解Java虛擬機是一本不錯的書,至少在理論上是的。這是我看了第二遍,由於理論的東西不常常看會淡忘,因此此次看的時候我也寫了筆記,但願本身以後看的時候能更快抓住重點。同時也但願其餘人能經過個人總結快速理解。

  這部分的內容基本把面試官喜歡問的問題都已經闡述清楚啦。須要咱們可以將對象的內存分配和回收的流程用本身的話說清楚,更重要的是叫別人能理解。

相關文章
相關標籤/搜索