|
一鍵注冊,加入手機圈
您需要 登錄 才可以下載或查看,沒有帳號?立即注冊 ![](source/plugin/mapp_wechat/images/wechat_login.png)
x
一、如何確定是垃圾
1、引用計數(shù)法
對象如果沒有與之關(guān)聯(lián)的引用,計數(shù)器為0的對象,就是可回收的對象。(目前python就使用)
優(yōu)點:判定效率高,實現(xiàn)簡單。
缺點:不完全準確,無法回收循環(huán)引用的對象,容易內(nèi)存泄漏。
2、可達性分析(根可達)
通過一系列GC Roots的對象作為起始點,從這些根節(jié)點開始向下搜,搜索所有走過的路叫做引用連,當一個對象到GC Roots沒有任何的引用鏈相連時,則說明此對象不可用。
優(yōu)點:解決相互循環(huán)引用問題。
注:不可達對象不等價于可回收對象,不可達對象變?yōu)榭苫厥諏ο笾辽僖?jīng)過兩次標記過程,可以通過finalize()自救。
3、GC Roots對象
GC Roots對象包括:
虛擬機棧棧幀中本地變量表引用的對象;
方法區(qū)中類靜態(tài)屬性引用;
方法區(qū)中常量引用的對象;
本地方法棧中JNI引用的對象;
所有被同步鎖持有的對象等;
jvm中跨代引用的對象等;
.......還有其他的幾種,常用的前四種。
二、垃圾回收算法
java是自動回收內(nèi)存的,C、c++等都要手工命令回收。
1、復制算法
按內(nèi)存容量將內(nèi)存劃分為大小相等的兩塊,每次使用一塊,這一塊滿后,將尚存活的復制到另一塊上去,把它使用的內(nèi)存清空掉。
優(yōu)點:實現(xiàn)簡單,不易產(chǎn)生碎片;
缺點:可用內(nèi)存被壓縮為原來的一半,且存活對象多的話,此算法效率大大降低。
2、標記清除算法(Mark-Sweep)
標記要回收的對象,回收被標記的對象占用的空間。
缺點:內(nèi)存碎片化嚴重;
3、標記整理算法(Mark-Compact)
標記要回收的對象,標記后不是清楚標記對象,而是將存活的對象移向內(nèi)存的一端,然后清楚邊界外的對象。
優(yōu)點:沒有碎片化;
4、分代收集算法
目前大部分JVM采用的【新生代、老年代、永久代】
根據(jù)對象存活的不同生命周期,將內(nèi)存劃分為不同的域,一般情況下,垃圾回收主要回收堆空間(因為幾乎大部分對象都在堆空間,特例逃逸分析:棧上分配),所以將堆劃分為新生代(1/3)、老年代(2/3)。
老年代:大對象直接放在老年代;長期存活的對象進入老年代;
每次只有少量對象需要被回收,存活率高,比較穩(wěn)定。所以老年代選擇標記整理算法或者標記清除算法。老年代的垃圾回收叫Major GC;
新生代:存放新生對象,對象朝生夕死,大量對象被回收,少量存活所以復制成本低,所以新生代選擇復制算法。新生代的垃圾回收叫Minor GC(復制-清空-互換)。
永久代:方法區(qū)的永生代,用來存儲class類、常量、方法描述等,對永生代的回收主要是廢棄的常量和無用的類。垃圾較少,收益一般較小,所以垃圾回收主要回收堆空間。1.8之后叫做元空間。
Java內(nèi)存模型
【面試必問系列】JVM垃圾回收算法、收集器看這一篇就夠了-1.jpg (119.19 KB, 下載次數(shù): 64)
下載附件
2023-4-6 16:44 上傳
- 其中新生代又劃分為一個eden區(qū),兩個survivor區(qū),默認比例為8:1:1。
- 新生對象分配在eden區(qū)。如果eden區(qū)的垃圾經(jīng)過一次GC幸存,就復制到survivor區(qū),從eden-->survivor 對象年齡+1,survivor-->eden 對象年齡+1,如果最后存活對象的年齡達到15將被移至老年代。
- 動態(tài)年齡:按照年齡從小到大對其所占用的大小進行累積,當累積的某個年齡大小超過了survivor區(qū)的一半時,取這個年齡和MaxTenuringThreshold中更小的一個值,作為新的晉升年齡閾值。eg:survivor區(qū)已經(jīng)有一半年齡為4的對象了,占用了一半survivor區(qū)內(nèi)存了,也將這些對象移至老年代。
注:Major GC前一般會先進行Minor GC,Minor GC頻繁被觸發(fā),Major GC不會,無法找到足夠大的連續(xù)空間分配給新創(chuàng)建的較大對象時,也會提前觸發(fā)Major GC;Major GC速度一般比Minor GC慢10倍以上;
Full GC :是清理整個堆空間。
觸發(fā)條件:
①手動條用System.gc()
②老年代空間不足;
③方法區(qū)空間不足;
④經(jīng)過Minor GC后,進行移動或分配的對象大小大于老年代可用空間。
小結(jié):分代收集算法就是根據(jù)不同的區(qū)域?qū)ο笊芷谔攸c選擇不同的回收算法;
垃圾回收主要回收堆內(nèi)存;
survivor區(qū)晉升年齡閾值有兩種情況,年齡15或者達到survivor區(qū)的50%。
三、GC 性能衡量指標
1、吞吐量:
這里的衡量吞吐量是指應用程序所花費的時間和系統(tǒng)總運行時間的比值。我們可以按照這個公式來計算 GC 的吞吐量:系統(tǒng)總運行時間 = 應用程序耗時+GC 耗時。如果系統(tǒng)運行了 100 分鐘,GC 耗時 1 分鐘,則系統(tǒng)吞吐量為 99%。GC 的吞吐量一般不能低于 95%。
2、停頓時間:
指垃圾回收器正在運行時,應用程序的暫停時間。對于串行回收器而言,停頓時間可能會比較長;而使用并發(fā)回收器,由于垃圾收集器和應用程序交替
運行,程序的停頓時間就會變短,但其效率很可能不如獨占垃圾收集器,系統(tǒng)的吞吐量也很可能會降低。
3、垃圾回收頻率:
通常垃圾回收的頻率越低越好,增大堆內(nèi)存空間可以有效降低垃圾回收發(fā)生的頻率,但同時也意味著堆積的回收對象越多,最終也會增加回收時的停頓時間。所以我們需要適當?shù)卦龃蠖褍?nèi)存空間,保證正常的垃圾回收頻率即可。
四、垃圾收集器
目前主流的7個:
新生代收集器:Serial、ParNew、Parallel Scavenge;
老年代收集器:Serial Old、Parallel Old、CMS;
整堆收集器:G1;
下面一個個介紹下這些收集器特點及作用:
1、Serial(單線程,復制算法,新生代)
Serial在收集垃圾時,必須暫停其他所有工作線程,直至垃圾回收結(jié)束,對于單個cpu來說,沒有線程交互的開銷,效率高。
因此Serial是java虛擬機運行在Client模式下默認的新生代垃圾收集器。
2、ParNew(Serial的并行的多線程版,復制算法,新生代)
ParNew除了使用多線程進行垃圾回收以外,其他行為和Serial一樣,也要暫停所有工作線程。
ParNew默認開啟和cpu數(shù)目相同的線程,可以通過參數(shù)-XX:ParallelGCThreads限制線程數(shù)量,是很多虛擬機在Server模式下新生代默認的垃圾收集器。
3、Parallel Scavenge(并行的多線程版,復制算法,新生代)
重點關(guān)注程序可達到的一個可控制的吞吐量,有自適應調(diào)節(jié)策略,提升用戶的
體驗。是1.8默認的新生代收集器。
吞吐量=cpu運行用戶代碼時間/cpu總消耗時間
自適應調(diào)節(jié)策略:
Parallel Scavenge收集器能夠配合自適應調(diào)節(jié)策略,把內(nèi)存管理的調(diào)優(yōu)任務交給虛擬機去完成。只需要把基本的內(nèi)存數(shù)據(jù)設(shè)置好(如-Xmx設(shè)置最大堆),然后使用MaxGCPauseMillis參數(shù)(更關(guān)注最大停頓時間)或GCTimeRatio參數(shù)(更關(guān)注吞吐量)給虛擬機設(shè)立一個優(yōu)化目標,那具體細節(jié)參數(shù)的調(diào)節(jié)工作就由虛擬機完成了。
1)java -XX:+PrintFlagsFinal 可以看到1.8默認的是 UseParallelGC
ParallelGC 默認的是 Parallel Scavenge(新生代)+ Parallel Old(老年代)
2)自適應調(diào)節(jié)策略也是Parallel Scavenge收集器與ParNew收集器的一個重要區(qū)別。
4、Serial Old(單線程,標記整理算法,老年代)
主要運行在Client模式下,java虛擬機默認的老年代垃圾收集器,
在Server模式下兩種用途:
①jdk1.5之前與新生代的Parallel Scavenge搭配使用;
②作為老年代中CMS收集器的后備垃圾收集方案。
5、Parallel Old(并行的多線程,標記整理算法,老年代)
若同樣考慮老年代的吞吐量,可以考慮搭配新生代的Parallel Scavenge使用。
6、CMS(Concurent Mark Sweep)(并發(fā)的多線程,標記清除算法、老年代)
主要目的是獲取最短垃圾回收停頓時間,可以為交互較高的程序提高用戶體驗。是目前老年代中唯一一個標記清除算法而不是標記整理算法的垃圾收集器。
缺點:cpu敏感、浮動垃圾、內(nèi)存碎片
CMS分為四個階段:
1)初始標記(暫停):只是標記一下GC Roots能直接關(guān)聯(lián)的對象,速度快,暫停所有工作線程;
2)并發(fā)標記(并發(fā)):進行GC Roots跟蹤,和用戶線程一起工作;
1)重新標記(暫停):修正并發(fā)標記期間,因程序運行導致的標記變動那部分對象的標記,暫停所有工作線程;
1)并發(fā)清除(并發(fā)):清除GC Roots不可達對象,和用戶線程一起工作。
所以總體上看,CMS收集器的內(nèi)存回收和用戶線程是一起并發(fā)執(zhí)行的。eg:web程序,B/S服務。
7、G1(Garbage first)
G1最突出改進:
1)基于標記整理算法,沒有碎片;
2)可以非常精準的控制停頓時間,在不犧牲吞吐量的前提下,實現(xiàn)低停頓的回收,讓使用者明確指定在一個長度為M毫秒的時間片段內(nèi),消耗在垃圾回收上的時間不得超過N毫秒。
G1是避免全區(qū)域的垃圾收集,而是將java堆劃分為多個大小固定的獨立區(qū)域,并且跟蹤這些區(qū)域里面的垃圾堆積程度,在后臺維護一個優(yōu)先列表,每次根據(jù)允許的收集時間,優(yōu)先回收垃圾最多的區(qū)域。
8、垃圾收集器的搭配
上述7種垃圾收集器的搭配使用,有連接的可以搭配。
【面試必問系列】JVM垃圾回收算法、收集器看這一篇就夠了-2.jpg (51.98 KB, 下載次數(shù): 53)
下載附件
2023-4-6 16:44 上傳
注意幾個概念:單線程、并行、并發(fā)是不一樣的。
并行(Parallel):指多條垃圾收集線程并行工作,但此時用戶線程依舊處于等待狀態(tài); 如ParNew、Parallel Scavenge、Parallel Old;
并發(fā)(Concurrent):指用戶線程與垃圾收集線程同時執(zhí)行(但不一定是并行的,可能會交替執(zhí)行),如CMS、G1;
CMS、G1的并發(fā)標記 :采用了三色標記法:分別是白色、灰色和黑色。三色標記最大的好處是可以異步執(zhí)行,從而可以以中斷時間極少的代價或者完全沒有中斷來進行整個 GC。
并發(fā)標記容易產(chǎn)品漏標問題,CMS從根從新掃描,G1快照方式對比差異,具體此處不多延伸。
五、安全點與安全區(qū)域
1、安全點
不是在任何時候都可以隨便GC的,當系統(tǒng)要進行垃圾回收時,業(yè)務線程不是立馬停下來的,可想而知立馬停下可能會有問題,為了準確安全地回收內(nèi)存,JVM是在Safe Point點時才進行回收,
就是業(yè)務線程去按照某種策略輪詢檢查這個變量一旦發(fā)現(xiàn)是安全點(Safe Point)就主動掛起,那樣當JVM達到Safe Point就可以安全準確的GC了。
安全點主要在以下位置設(shè)置:
1) 循環(huán)的末尾
2)方法返回前
3)調(diào)用方法的call之后
4) 拋出異常的位置
2、安全區(qū)域
若用戶線程sleep了等,不能主動檢測變量走向安全點,顯然JVM也不可能等待程序喚醒,這時候就需要安全區(qū)域了。
安全區(qū)域是指一段代碼片中,引用關(guān)系不會發(fā)生變化,在這個區(qū)域任何地方GC都是安全的,安全區(qū)域可以看做是安全點的一個擴展。線程執(zhí)行到安全區(qū)域的代碼時,首先標識自己進入了安全區(qū)域,這樣GC時就不用管進入安全區(qū)域的線層了,線程要離開安全區(qū)域時就檢查JVM是否完成了GC Roots枚舉,如果完成就繼續(xù)執(zhí)行,如果沒有完成就等待直到收到可以安全離開的信號。
----------------------------- |
|