垃圾回收主要內容:
1. 那些內存需要回收?
2. 什么時候回收?
3. 如何回收?
垃圾回收主要針對運行時數據區那些區域?
運行時數據區的線程私有區域有:虛擬機棧,本地方法棧,程序計數器等;
棧中的棧幀隨着方法的進入和退出執行入棧和出棧,每個棧幀的內存分配在編譯期就已經確定;
隨着線程或方法的結束,內存也隨着回收;
運行時數據區的線程共享區域有:方法區,堆;
方法區和堆只有程序處於運行期才能確定創建那些對象,因此這部分內存分配和回收都是動態的;
垃圾回收的重點區域;
一,對象存活判斷
1,引用計數算法
給對象添加一個引用計數器,被引用時計數器值+1,引用失效計數器值-1,當計數器值為0時對象不可能再被使用;
主流Java虛擬機未選用該算法管理內存(未解決對象之間相互循環引用的問題)
實現簡單,判斷效率高(應用:FlashPlayer,Python等)
2,可達性分析算法
將"GC Roots"對象作為起始節點,向下搜索,搜索走過的路徑為引用鏈;當一個對象到GC Roots沒有引用鏈時,則該對象是不可用的;
可作為"GC Roots"的對象:
【1】,方法區中靜態屬性引用的對象
【2】,方法區中常量引用的對象
【3】,虛擬機棧引用的對象 (棧幀中本地變量表)
【4】,本地方法棧中JNI引用的對象 (Native方法)
二,垃圾回收算法
1,標記-清除算法
定義:先標記要回收的對象,然后統一回收;
適用:存活對象較多的垃圾回收
缺點:
【1】,效率低; 標記和清除的過程效率不高;
【2】,空間問題; 標記清除后產生大量不連續的內存碎片,給大對象分配內存時沒有足夠連續的內存空間,導致提前出發垃圾回收動作。
2,復制算法
定義:將可用內存划分成相等大小兩塊,每次只使用其中一塊,當這一塊用完后將還存活的對象復制到另一塊,
然后將已使用過的內存一次清理。
適用:存活對象較少的垃圾回收
優點:每次對整個半區進行內存回收,不用考慮內存碎片問題,只要移動堆頂指針,按順序分配內存即可;
實現簡單,運行高效
缺點:將內存縮小了一半
其他:
將新生代內存按照8:1:1分為Eden,From Survivor,To Survivor三個空間,每次使用Eden和From Survivor兩個空間給對象分配內存,
當內存不足垃圾回收時,將存活對象復制到To Survivor空間,然后清理Eden和From Survivor空間;這樣相當於內存指浪費了10%;
如果10%的To Survivor空間不夠存放存活對象時需要老年代進行分配擔保(將存活對象通過分配擔保機制直接進入老年代)
3,標記-整理算法
定義:先標記要回收的對象,將存活對象移至一端,最后清理端邊界以外的內存
4,分代收集算法
定義:根據對象存活周期將內存划分為新生代和老年代,然后根據每個年代的特點使用合適的回收算法;
如:新生代存活對象少可以采用復制算法; 老年代存活對象多並且沒有分配擔保必須使用標記清理或標記整理回收算法
三,垃圾回收器
1,Serial收集器
定義:單線程收集器,收集時必須暫停其他所有用戶線程,直到收集結束。
適用:新生代
配置:
-XX:PretenureSizeThreshold
-XX:HandlePromotionFailure
其他:
【1】,單CPU環境Serial收集器沒有現成交互開銷,因此單線程的收集效率最高
【2】,對於Client模式下的桌面應用,分配給虛擬機的內存不會很大,對於一兩百兆的新生代內存回收停頓時間完全控制在一百多毫秒以內,
停頓不頻繁發生,Serial收集器是最好的選擇;
【3】,收集過程會暫停服務(Stop the world)
2,ParNew收集器
定義:是Serial收集器的多線程版本
適用:新生代
配置:
-XX:PretenureSizeThreshold
-XX:HandlePromotionFailure
-XX:+UseConcMarkSweepGC (設置默認新生代收集器)
-XX:+UserParNewGC (指定ParNew作為新生代收集器)
-XX:ParallelGCThreads(限制垃圾收集的線程數)
其他:
【1】,與Serial收集器的控制參數,收集算法,Stop the world,對象分配規則,回收策略完全一樣
【2】,是運行Server模式下虛擬機首選新生代收集器(唯一能和CMS收集器配合工作)
CMS是並發收集器,第一次實現讓收集線程和用戶線程同時工作;
CMS屬老年代收集器,無法與Parallel Scavenge配合工作;
CMS關注回收的停頓時間(暫停用戶線程時間),停頓時間越短越適合於用戶交互的程序,因為有較高的響應速度
【3】,單CPU環境沒有Serial收集器效率高
【4】,並行的多線程收集器
3,Parallel Scavenge收集器
定義:和ParNew收集器一樣的收集器,區別於主要關注吞吐量的控制和GC自適應調節策略;
注: 吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間) ; 如果虛擬機總共運行100分鍾,收集花費1分鍾,則吞吐量為99%
適用:新生代; 較高的吞吐量,導致高效率的CPU利用率,主要適合后台運算
配置:
-XX:MaxGCPauseMillis (控制回收停頓的最大時間 ; 注:時間越小會犧牲吞吐量和新生代空間)
-XX:GCTimeRatio (設置吞吐量 0< X < 100 ; 垃圾回收時間占總時間的比例, 吞吐量的倒數)
如果將GCTimeRatio設置為19,最大GC時間占總時間的5%(即1/(1+19))
該值默認為99,則最大GC時間為1%
-XX:+UseAdaptiveSizePolicy (打開GC自適應調節策略)
GC自適應調節策略:虛擬機根據當前系統性能,自動調節參數已提供最合適的時間和最大吞吐量;
調節的參數包括:①,新生代大小(-Xmn),Eden和Survivor空間比例(-XX:SurvivorRatio)
②,晉升老年代對象的年齡(-XX:PretenureSizeThreshold)
... ...
GC自適應調節策略將內存管理交給虛擬機完成,只需要設置基本內存(-Xmx),停頓時間(MaxGCPauseMillis),吞吐量 (GCTimeRatio)等參數給虛擬機設立優化目標;
其他:
【1】並行:多條垃圾回收線程並行工作,單用戶線程處於等待狀態
並發:用戶線程和回收線程同時進行
【2】,並行的多線程收集器
4,Serial Old收集器
定義:是Serial收集器的老年代版本的單線程收集器
適用:老年代(標記-整理算法); 主要給Client模式下的虛擬機使用;
其他:
如果在Server模式下主要有兩大用途:①,在JDK1.5及之前的版本中與Parallel Scavenge收集器搭配使用
②,作為CMS收集器的后備預案(並發收集發生Concurrent Mode Failure時使用)
5,Parallel Old收集器
定義:是Parallel Scavenge收集器的老年代版本的多線程收集器
使用:老年代(標記-整理算法)
其他:
【1】,JDK1.6開始提供該收集器
6,CMS收集器(Concurrent Mark Sweep)
定義:CMS是一款並發收集,低停頓的收集器
關注目標:最短回收停頓時間
適用:互聯網站,B/S系統服務端(較快的響應速度,最短的系統停頓時間)較好的用戶體驗
算法:標記-清除
回收步驟:
【1】初始標記(標記GC Roots能直接關聯到的對象)(stop the world)
【2】並發標記(進行GC Roots Tracing的過程)
【3】重新標記(修正並發標記期間因用戶程序繼續運行而導致標記產生變動的對象的標記記錄)(stop the world)
【4】並發清除
執行時間:
T(並發標記,並發清除) > T(重新標記) > T(初始標記)
由於耗時最長的並發標記和並發清除和用戶線程一起工作,因此總體上CMS回收過程和用戶線程一起並發執行的。
配置:
-XX:+UseConcMarkSweepGC 使用CMS收集器
-XX:ParallelCMSThreads 設定CMS的線程數量(一般情況約等於可用CPU數量)
缺點:
【1】對CPU資源非常敏感
並發階段會占用部分線程(CPU資源)導致應用程序變慢,總吞吐量降低。
CMS默認回收線程數 = (CPU數量 + 3)/4; CPU數量↑回收線程↓; CPU數量=4時回收線程占用25%CPU資源;
CPU數量 < 4時,如CPU數量=2,則回收線程會占用一半CPU資源,導致用戶程序執行速度直接降低50%,i-CMS收集器可以解決此問題;
增量式並發收集器(i-CMS): 並發標記和並發清理時讓GC線程和用戶線程交替運行,盡量減少GC線程獨占資源的時間
【2】無法處理浮動垃圾,可能出現Concurrent Mode Failure失敗而產生Full GC
並發清理時用戶線程還在運行而不斷產生的垃圾,由於在標記之后,CMS無法當次收集清理,只能等下次GC時清理,這部分垃圾稱為“浮動垃圾”;
由於當次無法處理浮動垃圾,這些浮動垃圾又占有一定內存,又考慮要預留給用戶線程足夠的內存,因此讓老年代提供預留空間;
因此CMS默認老年代使用68%時觸發一次回收,百分比配置(-XX:CMSInitiatingOccupancyFraction)
如果CMSInitiatingOccupancyFraction參數設置過高,預留空間不足就會出現Concurrent Mode Failure失敗,
此時虛擬機啟動Serial Old收集器進行老年代垃圾回收停頓時間變長,性能降低。
【3】回收后空間碎片過多
因為CMS采用標記-清除算法因此回收后會產生大量空間碎片,無法給大對象分配連續內存空間而觸發Full GC;
+UseCMSCompactAtFullCollection參數,用於在觸發Full GC之前開啟內存碎片整理過程,整理階段不能並發,因此停頓時間加長。
-XX:CMSFullGCsBeforeCompaction參數,設置進行多少次不整理的Full GC之后,進行一次帶整理的Full GC。
7,G1收集器
定義:一款面向服務端應用的垃圾收集器
算法:標記-整理
特點:
【1】並行與並發
充分利用多CPU,多核環境縮短Stop the World停頓時間,使用並發方式回收避免了GC時停頓java線程
【2】分代收集
【3】空間整合(使用了標記-整理回收算法,避免了大量空間內存碎片的產生)
【4】可預測的停頓(G1在追求低停頓同時建立了可預測的停頓時間模型,可以讓使用者設置M毫秒內,GC所需要的時間不超過N毫秒)
原理:
1,G1將新生代和老年代分為大小相等的獨立區域,進行全區域垃圾回收,新生代和老年代不再是物理隔離,都是部分獨立區域的集合;
2,通過計算每個區域垃圾堆積的價值(回收可得到的空間/回收所需要的時間),然后根據價值大小有優先級地進行垃圾回收,保證了回收的效率;
3,虛擬機發現獨立區域中的Reference類型數據進行寫操作時,判斷其他獨立區域是否有Reference數據引用的對象(即老年代對象是否引用了新生代對象),
如果是就將引用信息通過CardTable記錄到獨立區域的Remembered Set中,在垃圾回收的時候將Remembered Set加入到GC根節點的枚舉范圍,可避免使用
可達性算法判斷對象存活而進行的全堆掃描,也避免有存活對象的遺漏。
回收步驟:
【1】初始標記(標記GC Roots能直接關聯到的對象,並修改TAMS(Next Top at Mark Start)的值,讓下一階段用戶程序並發運行時,能在正確的Region中創建對象)(Stop the World)
【2】並發標記(從GC Roots開始對堆中對象可達性分析,找出存活對象)(和用戶線程並發進行)
【3】最終標記(修正並發標記期間因用戶線程運行的而產生變動對象的標記記錄,虛擬機將這些對象記錄到Remembered Set Logs中,然后合並到Rembered Set中)(Stop the World)
【4】篩選回收(對每個獨立區域進行價值排序,根據用戶期望的GC停頓時間制定回收計划)
四,回收器的使用
1,參數
UseSerialGC(使用Serial + Serial Old組合收集器)
UseParNewGC(使用ParNew + Serial Old組合收集器)
UseConcMarkSweepGC(使用ParNew + CMS + Serial Old組合收集器; Serial Old是CMS出現Concurrent Mode Failure失敗后備用收集器)
UseParallelGC(使用Parallel Scavenge + Serial Old組合收集器)
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在老年代空間被使用多少后觸發一次GC, 默認68%, 僅使用CMS收集器生效)
UseCMSCompactAtFullCollection(設置CMS完成回收后是否要進行一次內存碎片整理, 僅使用CMS收集器生效)
CMSFullGCsBeforeCompaction(設置CMS在進行若干次回收后再啟動一次內存碎片整理)
-XX:+PrintGCDetails(打印虛擬機日志)
2,回收器的組合使用
|
新生代
|
年老代
|
說明
|
組合1
|
Serial
|
Serial Old
|
都是單線程進行GC; GC時暫停所有應用線程
|
組合2
|
Serial
|
CMS+Serial Old
|
CMS是並發GC,和應用線程並發工作,不需要暫停所有應用線程
CMS出現Concurrent Mode Failure失敗后使用Serial Old收集器
|
組合3
|
ParNew
|
CMS
|
用-XX:+UseParNewGC選項來開啟
ParNew是Serial的並行版本,可以指定GC線程數,默認GC線程數為CPU的數量
使用-XX:ParallelGCThreads選項指定GC的線程數
用-XX:+UseConcMarkSweepGC選項,則新生代默認使用ParNew GC策略。
|
組合4
|
ParNew
|
Serial Old
|
用-XX:+UseParNewGC選項來開啟
|
組合5
|
Parallel Scavenge
|
Serial Old
|
Parallel Scavenge策略主要是關注一個可控的吞吐量:應用程序運行時間 / (應用程序運行時間 + GC時間);
會使得CPU的利用率盡可能的高,適用於后台持久運行的應用程序,而不適用於交互較多的應用程序。
|
組合6
|
Parallel Scavenge
|
Parallel Old
|
Parallel Old是Serial Old的並行版本
|
組合7
|
G1
|
G1
|
-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC 開啟
-XX:MaxGCPauseMillis =50 暫停時間目標
-XX:GCPauseIntervalMillis =200 暫停間隔目標
-XX:+G1YoungGenSize=512m 年輕代大小
-XX:SurvivorRatio=6 新生代中Eden與Survivor容量比值
|