垃圾收集主要是針對(duì)堆和方法區(qū)進(jìn)行。程序計(jì)數(shù)器、虛擬機(jī)棧和本地方法棧這三個(gè)區(qū)域?qū)儆诰€程私有的,只存在于線程的生命周期內(nèi),線程結(jié)束之后就會(huì)消失,因此不需要對(duì)這三個(gè)區(qū)域進(jìn)行垃圾回收。
成都創(chuàng)新互聯(lián)公司成都網(wǎng)站建設(shè)按需制作,是成都網(wǎng)站營銷公司,為不銹鋼雕塑提供網(wǎng)站建設(shè)服務(wù),有成熟的網(wǎng)站定制合作流程,提供網(wǎng)站定制設(shè)計(jì)服務(wù):原型圖制作、網(wǎng)站創(chuàng)意設(shè)計(jì)、前端HTML5制作、后臺(tái)程序開發(fā)等。成都網(wǎng)站維護(hù)熱線:028-869222201. 引用計(jì)數(shù)算法
為對(duì)象添加一個(gè)引用計(jì)數(shù)器,當(dāng)對(duì)象增加一個(gè)引用時(shí)計(jì)數(shù)器加 1,引用失效時(shí)計(jì)數(shù)器減 1。引用計(jì)數(shù)為 0 的對(duì)象可被回收。
在兩個(gè)對(duì)象出現(xiàn)循環(huán)引用的情況下,此時(shí)引用計(jì)數(shù)器永遠(yuǎn)不為 0,導(dǎo)致無法對(duì)它們進(jìn)行回收。正是因?yàn)檠h(huán)引用的存在,因此 Java 虛擬機(jī)不使用引用計(jì)數(shù)算法。
public class Test {
public Object instance = null;
public static void main(String[] args) {
Test a = new Test();
Test b = new Test();
a.instance = b;
b.instance = a;
a = null;
b = null;
doSomething();
}
}
在上述代碼中,a 與 b 引用的對(duì)象實(shí)例互相持有了對(duì)象的引用,因此當(dāng)我們把對(duì) a 對(duì)象與 b 對(duì)象的引用去除之后,由于兩個(gè)對(duì)象還存在互相之間的引用,導(dǎo)致兩個(gè) Test 對(duì)象無法被回收。
2. 可達(dá)性分析算法
通過判斷對(duì)象的引用鏈?zhǔn)欠窨蛇_(dá)來決定對(duì)象是否可以被回收。
以 GC Roots 為起始點(diǎn)進(jìn)行搜索,可達(dá)的對(duì)象都是存活的,不可達(dá)的對(duì)象可被回收。
Java 虛擬機(jī)使用該算法來判斷對(duì)象是否可被回收,GC Roots 一般包含以下內(nèi)容:
3. 方法區(qū)的回收
因?yàn)榉椒▍^(qū)主要存放永久代對(duì)象,而永久代對(duì)象的回收率比新生代低很多,所以在方法區(qū)上進(jìn)行回收性價(jià)比不高。
主要是對(duì)常量池的回收和對(duì)類的卸載。
為了避免內(nèi)存溢出,在大量使用反射和動(dòng)態(tài)代理的場景都需要虛擬機(jī)具備類卸載功能。
類的卸載條件很多,需要滿足以下三個(gè)條件,并且滿足了條件也不一定會(huì)被卸載:
4. finalize()
類似 C++ 的析構(gòu)函數(shù),用于關(guān)閉外部資源。但是 try-finally 等方式可以做得更好,并且該方法運(yùn)行代價(jià)很高,不確定性大,無法保證各個(gè)對(duì)象的調(diào)用順序,因此最好不要使用。
當(dāng)一個(gè)對(duì)象可被回收時(shí),如果需要執(zhí)行該對(duì)象的 finalize() 方法,那么就有可能在該方法中讓對(duì)象重新被引用,從而實(shí)現(xiàn)自救。自救只能進(jìn)行一次,如果回收的對(duì)象之前調(diào)用了 finalize() 方法自救,后面回收時(shí)不會(huì)再調(diào)用該方法。
Object 的finalize()方法的作用是否與C++的析構(gòu)函數(shù)作用相同?
無論是通過引用計(jì)數(shù)算法判斷對(duì)象的引用數(shù)量,還是通過可達(dá)性分析算法判斷對(duì)象是否可達(dá),判定對(duì)象是否可被回收都與引用有關(guān)。
Java 提供了四種強(qiáng)度不同的引用類型。
1. 強(qiáng)引用
被強(qiáng)引用關(guān)聯(lián)的對(duì)象不會(huì)被回收。
使用 new 一個(gè)新對(duì)象的方式來創(chuàng)建強(qiáng)引用。
Object obj = new Object();
拋出OOM Error終止程序也不會(huì)回收具有強(qiáng)引用的對(duì)象,只有通過將對(duì)象設(shè)置為null來弱化引用,才能使其被回收。
2. 軟引用
表示對(duì)象處在有用但非必須的狀態(tài)。
被軟引用關(guān)聯(lián)的對(duì)象只有在內(nèi)存不夠的情況下才會(huì)被回收??梢杂脕韺?shí)現(xiàn)內(nèi)存敏感的高速緩存。
使用 SoftReference 類來創(chuàng)建軟引用。
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null; // 使對(duì)象只被軟引用關(guān)聯(lián)
3. 弱引用
表示非必須的對(duì)象,比軟引用更弱一些。適用于偶爾被使用且不影響垃圾收集的對(duì)象。
被弱引用關(guān)聯(lián)的對(duì)象一定會(huì)被回收,也就是說它只能存活到下一次垃圾回收發(fā)生之前。
使用 WeakReference 類來創(chuàng)建弱引用。
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
4. 虛引用
又稱為幽靈引用或者幻影引用,一個(gè)對(duì)象是否有虛引用的存在,不會(huì)對(duì)其生存時(shí)間造成影響,也無法通過虛引用得到一個(gè)對(duì)象。
不會(huì)決定對(duì)象的生命周期,任何時(shí)候都可能被垃圾回收器回收。必須和引用隊(duì)列ReferenceQueue聯(lián)合使用。
為一個(gè)對(duì)象設(shè)置虛引用的唯一目的是能在這個(gè)對(duì)象被回收時(shí)收到一個(gè)系統(tǒng)通知,起哨兵作用。具體來說,就是通過判斷引用隊(duì)列ReferenceQueue是否加入虛引用來判斷被引用對(duì)象是否被GC回收。
使用 PhantomReference 來創(chuàng)建虛引用。
Object obj = new Object();
ReferenceQueue queue = new ReferenceQueue();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, queue);
obj = null;
引用隊(duì)列(ReferenceQueue):當(dāng)GC(垃圾回收線程)準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還僅有軟引用(或弱引用,或虛引用)指向它,就會(huì)在回收該對(duì)象之前,把這個(gè)軟引用(或弱引用,或虛引用)加入到與之關(guān)聯(lián)的引用隊(duì)列(ReferenceQueue)中。如果一個(gè)軟引用(或弱引用,或虛引用)對(duì)象本身在引用隊(duì)列中,就說明該引用對(duì)象所指向的對(duì)象被回收了。無實(shí)際的存儲(chǔ)結(jié)構(gòu),存儲(chǔ)邏輯依賴于內(nèi)部節(jié)點(diǎn)之間的關(guān)系來表達(dá)。
1. 標(biāo)記 - 清除
在標(biāo)記階段,從根集合進(jìn)行掃描,會(huì)檢查每個(gè)對(duì)象是否為活動(dòng)對(duì)象,如果是活動(dòng)對(duì)象,則程序會(huì)在對(duì)象頭部打上標(biāo)記。
在清除階段,會(huì)進(jìn)行對(duì)象回收并取消標(biāo)志位,另外,還會(huì)判斷回收后的分塊與前一個(gè)空閑分塊是否連續(xù),若連續(xù),會(huì)合并這兩個(gè)分塊。回收對(duì)象就是把對(duì)象作為分塊,連接到被稱為 “空閑鏈表” 的單向鏈表,之后進(jìn)行分配時(shí)只需要遍歷這個(gè)空閑鏈表,就可以找到分塊。
在分配時(shí),程序會(huì)搜索空閑鏈表尋找空間大于等于新對(duì)象大小 size 的塊 block。如果它找到的塊等于 size,會(huì)直接返回這個(gè)分塊;如果找到的塊大于 size,會(huì)將塊分割成大小為 size 與 (block - size) 的兩部分,返回大小為 size 的分塊,并把大小為 (block - size) 的塊返回給空閑鏈表。
不足:
2. 標(biāo)記 - 整理
讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。
優(yōu)點(diǎn):
不足:
3. 復(fù)制
將內(nèi)存劃分為大小相等的兩塊,每次只使用其中一塊,當(dāng)這一塊內(nèi)存用完了就將還存活的對(duì)象復(fù)制到另一塊上面,然后再把使用過的內(nèi)存空間進(jìn)行一次清理。
主要不足是只使用了內(nèi)存的一半。
現(xiàn)在的商業(yè)虛擬機(jī)都采用這種收集算法回收新生代,但是并不是劃分為大小相等的兩塊,而是一塊較大的 Eden 空間和兩塊較小的 Survivor 空間,每次使用 Eden 和其中一塊 Survivor。在回收時(shí),將 Eden 和 Survivor 中還存活著的對(duì)象全部復(fù)制到另一塊 Survivor 上,最后清理 Eden 和使用過的那一塊 Survivor。
HotSpot 虛擬機(jī)的 Eden 和 Survivor 大小比例默認(rèn)為 8:1,保證了內(nèi)存的利用率達(dá)到 90%。如果每次回收有多于 10% 的對(duì)象存活,那么一塊 Survivor 就不夠用了,此時(shí)需要依賴于老年代進(jìn)行空間分配擔(dān)保,也就是借用老年代的空間存儲(chǔ)放不下的對(duì)象。
4. 分代收集
Stop-the-World
Safepoint
分析過程中對(duì)象引用關(guān)系不會(huì)發(fā)生變化的點(diǎn);
產(chǎn)生Safepoint的地方:方法調(diào)用;循環(huán)跳轉(zhuǎn);異常跳轉(zhuǎn)等
現(xiàn)在的商業(yè)虛擬機(jī)采用分代收集算法,它根據(jù)對(duì)象存活周期將內(nèi)存劃分為幾塊,不同塊采用適當(dāng)?shù)氖占惴ā?
一般將堆分為新生代和老年代。
以上是 HotSpot 虛擬機(jī)中的 7 個(gè)垃圾收集器,連線表示垃圾收集器可以配合使用。
1. Serial 收集器(-XX:+UseSerialGC)
Serial 翻譯為串行,也就是說它以串行的方式執(zhí)行。
它是單線程的收集器,只會(huì)使用一個(gè)線程進(jìn)行垃圾收集工作。
它的優(yōu)點(diǎn)是簡單高效,在單個(gè) CPU 環(huán)境下,由于沒有線程交互的開銷,因此擁有最高的單線程收集效率。
它是 Client 場景下的默認(rèn)新生代收集器,因?yàn)樵谠搱鼍跋聝?nèi)存一般來說不會(huì)很大。它收集一兩百兆垃圾的停頓時(shí)間可以控制在一百多毫秒以內(nèi),只要不是太頻繁,這點(diǎn)停頓時(shí)間是可以接受的。
2. ParNew 收集器(-XX:+UseParNewGC)
它是 Serial 收集器的多線程版本。
它是 Server 場景下默認(rèn)的新生代收集器,除了性能原因外,主要是因?yàn)槌?Serial 收集器,只有它能與 CMS 收集器配合使用。
3. Parallel Scavenge 收集器(-XX:+UseParallelGC)
與 ParNew 一樣是多線程收集器。
其它收集器目標(biāo)是盡可能縮短垃圾收集時(shí)用戶線程的停頓時(shí)間,而它的目標(biāo)是達(dá)到一個(gè)可控制的吞吐量,因此它被稱為“吞吐量優(yōu)先”收集器。這里的吞吐量指 CPU 用于運(yùn)行用戶程序的時(shí)間占總時(shí)間的比值。
停頓時(shí)間越短就越適合需要與用戶交互的程序,良好的響應(yīng)速度能提升用戶體驗(yàn)。而高吞吐量則可以高效率地利用 CPU 時(shí)間,盡快完成程序的運(yùn)算任務(wù),適合在后臺(tái)運(yùn)算而不需要太多交互的任務(wù)。
縮短停頓時(shí)間是以犧牲吞吐量和新生代空間來換取的:新生代空間變小,垃圾回收變得頻繁,導(dǎo)致吞吐量下降。
可以通過一個(gè)開關(guān)參數(shù)打開 GC 自適應(yīng)的調(diào)節(jié)策略(GC Ergonomics),就不需要手工指定新生代的大?。?Xmn)、Eden 和 Survivor 區(qū)的比例、晉升老年代對(duì)象年齡等細(xì)節(jié)參數(shù)了。虛擬機(jī)會(huì)根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間或者大的吞吐量。
4. Serial Old 收集器(-XX:+UseSerialOldGC)
是 Serial 收集器的老年代版本,也是給 Client 場景下的虛擬機(jī)使用。如果用在 Server 場景下,它有兩大用途:
5. Parallel Old 收集器(-XX:+UseParallelOldGC)
是 Parallel Scavenge 收集器的老年代版本。
在注重吞吐量以及 CPU 資源敏感的場合,都可以優(yōu)先考慮 Parallel Scavenge 加 Parallel Old 收集器。
6. CMS 收集器(-XX:+UseConcMarkSweepGC)
CMS(Concurrent Mark Sweep),Mark Sweep 指的是標(biāo)記 - 清除算法。
分為以下六個(gè)流程:
具有以下缺點(diǎn):
7. G1 收集器(-XX:+UseG1GC)
G1(Garbage-First),它是一款面向服務(wù)端應(yīng)用的垃圾收集器,在多 CPU 和大內(nèi)存的場景下有很好的性能。HotSpot 開發(fā)團(tuán)隊(duì)賦予它的使命是未來可以替換掉 CMS 收集器。
堆被分為新生代和老年代,其它收集器進(jìn)行收集的范圍都是整個(gè)新生代或者老年代,而 G1 可以直接對(duì)新生代和老年代一起回收。
G1 把堆劃分成多個(gè)大小相等的獨(dú)立區(qū)域(Region),新生代和老年代不再物理隔離。
通過引入 Region 的概念,從而將原來的一整塊內(nèi)存空間劃分成多個(gè)的小空間,使得每個(gè)小空間可以單獨(dú)進(jìn)行垃圾回收。這種劃分方法帶來了很大的靈活性,使得可預(yù)測的停頓時(shí)間模型成為可能。通過記錄每個(gè) Region 垃圾回收時(shí)間以及回收所獲得的空間(這兩個(gè)值是通過過去回收的經(jīng)驗(yàn)獲得),并維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)允許的收集時(shí)間,優(yōu)先回收價(jià)值大的 Region。
每個(gè) Region 都有一個(gè) Remembered Set,用來記錄該 Region 對(duì)象的引用對(duì)象所在的 Region。通過使用 Remembered Set,在做可達(dá)性分析的時(shí)候就可以避免全堆掃描。
如果不計(jì)算維護(hù) Remembered Set 的操作,G1 收集器的運(yùn)作大致可劃分為以下幾個(gè)步驟:
具備如下特點(diǎn):
1. 使用 CMS 收集器
CMS 收集器進(jìn)行垃圾回收,有 4 個(gè)步驟:
其中初始標(biāo)記和重新標(biāo)記需要 “stop the world”,但耗時(shí)時(shí)間最長的并發(fā)標(biāo)記、并發(fā)清除過程中,GC 線程都可與用戶線程一起工作。整體上說,CMS 和用戶線程是并行的。
2. 增量算法
基本思路:若一次性將所有垃圾進(jìn)行處理,會(huì)造成系統(tǒng)長時(shí)間的停頓,則就讓 GC 線程與用戶線程交替執(zhí)行。每次 GC 線程只收集一小塊區(qū)域的內(nèi)存空間,接著切換到用戶線程,重復(fù)幾次,直至 GC 完成。
問題:存在線程切換和上下文切換,造成系統(tǒng)吞吐量下降。
名稱欄目:詳解Java虛擬機(jī)(第⑤篇)——垃圾收集-創(chuàng)新互聯(lián)
新聞來源:http://www.muchs.cn/article14/epdde.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序、虛擬主機(jī)、網(wǎng)站排名、微信公眾號(hào)、靜態(tài)網(wǎng)站、自適應(yīng)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容