日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入理解JVM垃圾收集机制,下次面试你准备好了吗

發(fā)布時(shí)間:2025/3/15 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解JVM垃圾收集机制,下次面试你准备好了吗 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

程序計(jì)數(shù)器、虛擬機(jī)棧和本地方法棧這三個(gè)區(qū)域?qū)儆诰€程私有的,只存在于線程的生命周期內(nèi),線程結(jié)束之后也會(huì)消失,因此不需要對(duì)這三個(gè)區(qū)域進(jìn)行垃圾回收。垃圾回收主要是針對(duì) Java 堆和方法區(qū)進(jìn)行。

判斷一個(gè)對(duì)象是否可回收

1. 引用計(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)行回收。

public?class?ReferenceCountingGC?{public?Object?instance?=?null;public?static?void?main(String[]?args)?{ReferenceCountingGC?objectA?=?new?ReferenceCountingGC();ReferenceCountingGC?objectB?=?new?ReferenceCountingGC();objectA.instance?=?objectB;objectB.instance?=?objectA;} }

正因?yàn)檠h(huán)引用的存在,因此 Java 虛擬機(jī)不適用引用計(jì)數(shù)算法。

2. 可達(dá)性分析算法

通過 GC Roots 作為起始點(diǎn)進(jìn)行搜索,能夠到達(dá)到的對(duì)象都是存活的,不可達(dá)的對(duì)象可被回收。

Java 虛擬機(jī)使用該算法來判斷對(duì)象是否可被回收,在 Java 中 GC Roots 一般包含以下內(nèi)容:

  • 虛擬機(jī)棧中引用的對(duì)象

  • 本地方法棧中引用的對(duì)象

  • 方法區(qū)中類靜態(tài)屬性引用的對(duì)象

  • 方法區(qū)中的常量引用的對(duì)象

3. 引用類型

無論是通過引用計(jì)算算法判斷對(duì)象的引用數(shù)量,還是通過可達(dá)性分析算法判斷對(duì)象的引用鏈?zhǔn)欠窨蛇_(dá),判定對(duì)象是否可被回收都與引用有關(guān)。

Java 具有四種強(qiáng)度不同的引用類型。

(一)強(qiáng)引用

被強(qiáng)引用關(guān)聯(lián)的對(duì)象不會(huì)被垃圾收集器回收。

使用 new 一個(gè)新對(duì)象的方式來創(chuàng)建強(qiáng)引用。

Object?obj?=?new?Object();

(二)軟引用

被軟引用關(guān)聯(lián)的對(duì)象,只有在內(nèi)存不夠的情況下才會(huì)被回收。

使用 SoftReference 類來創(chuàng)建軟引用。

Object?obj?=?new?Object(); SoftReference<Object>?sf?=?new?SoftReference<Object>(obj); obj?=?null;?//?使對(duì)象只被軟引用關(guān)聯(lián)

(三)弱引用

被弱引用關(guān)聯(lián)的對(duì)象一定會(huì)被垃圾收集器回收,也就是說它只能存活到下一次垃圾收集發(fā)生之前。

使用 WeakReference 類來實(shí)現(xiàn)弱引用。

Object?obj?=?new?Object(); WeakReference<Object>?wf?=?new?WeakReference<Object>(obj); obj?=?null;

WeakHashMap 的 Entry 繼承自 WeakReference,主要用來實(shí)現(xiàn)緩存。

private?static?class?Entry<K,V>?extends?WeakReference<Object>?implements?Map.Entry<K,V>

Tomcat 中的 ConcurrentCache 就使用了 WeakHashMap 來實(shí)現(xiàn)緩存功能。ConcurrentCache 采取的是分代緩存,經(jīng)常使用的對(duì)象放入 eden 中,而不常用的對(duì)象放入 longterm。eden 使用 ConcurrentHashMap 實(shí)現(xiàn),longterm 使用 WeakHashMap,保證了不常使用的對(duì)象容易被回收。

public?final?class?ConcurrentCache<K,?V>?{private?final?int?size;private?final?Map<K,?V>?eden;private?final?Map<K,?V>?longterm;public?ConcurrentCache(int?size)?{this.size?=?size;this.eden?=?new?ConcurrentHashMap<>(size);this.longterm?=?new?WeakHashMap<>(size);}public?V?get(K?k)?{V?v?=?this.eden.get(k);if?(v?==?null)?{v?=?this.longterm.get(k);if?(v?!=?null)this.eden.put(k,?v);}return?v;}public?void?put(K?k,?V?v)?{if?(this.eden.size()?>=?size)?{this.longterm.putAll(this.eden);this.eden.clear();}this.eden.put(k,?v);} }

(四)虛引用

又稱為幽靈引用或者幻影引用。一個(gè)對(duì)象是否有虛引用的存在,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響,也無法通過虛引用取得一個(gè)對(duì)象實(shí)例。

為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知。

使用 PhantomReference 來實(shí)現(xiàn)虛引用。

Object?obj?=?new?Object(); PhantomReference<Object>?pf?=?new?PhantomReference<Object>(obj); obj?=?null;

4. 方法區(qū)的回收

因?yàn)榉椒▍^(qū)主要存放永久代對(duì)象,而永久代對(duì)象的回收率比新生代差很多,因此在方法區(qū)上進(jìn)行回收性價(jià)比不高。

主要是對(duì)常量池的回收和對(duì)類的卸載。

類的卸載條件很多,需要滿足以下三個(gè)條件,并且滿足了也不一定會(huì)被卸載:

  • 該類所有的實(shí)例都已經(jīng)被回收,也就是 Java 堆中不存在該類的任何實(shí)例。

  • 加載該類的 ClassLoader 已經(jīng)被回收。

  • 該類對(duì)應(yīng)的 java.lang.Class 對(duì)象沒有在任何地方被引用,也就無法在任何地方通過反射訪問該類方法。

可以通過 -Xnoclassgc 參數(shù)來控制是否對(duì)類進(jìn)行卸載。

在大量使用反射、動(dòng)態(tài)代理、CGLib 等 ByteCode 框架、動(dòng)態(tài)生成 JSP 以及 OSGi 這類頻繁自定義 ClassLoader 的場景都需要虛擬機(jī)具備類卸載功能,以保證不會(huì)出現(xiàn)內(nèi)存溢出。

5. finalize()

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)自救。

垃圾收集算法

1. 標(biāo)記 - 清除

將需要回收的對(duì)象進(jìn)行標(biāo)記,然后清理掉被標(biāo)記的對(duì)象。

不足:

  • 標(biāo)記和清除過程效率都不高;

  • 會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,導(dǎo)致無法給大對(duì)象分配內(nèi)存。

2. 標(biāo)記 - 整理

讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。

3. 復(fù)制

將內(nèi)存劃分為大小相等的兩塊,每次只使用其中一塊,當(dāng)這一塊內(nèi)存用完了就將還存活的對(duì)象復(fù)制到另一塊上面,然后再把使用過的內(nèi)存空間進(jìn)行一次清理。

主要不足是只使用了內(nèi)存的一半。

現(xiàn)在的商業(yè)虛擬機(jī)都采用這種收集算法來回收新生代,但是并不是將內(nèi)存劃分為大小相等的兩塊,而是分為一塊較大的 Eden 空間和兩塊較小的 Survior 空間,每次使用 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. 分代收集

現(xiàn)在的商業(yè)虛擬機(jī)采用分代收集算法,它根據(jù)對(duì)象存活周期將內(nèi)存劃分為幾塊,不同塊采用適當(dāng)?shù)氖占惴ā?/p>

一般將 Java 堆分為新生代和老年代。

  • 新生代使用:復(fù)制算法

  • 老年代使用:標(biāo)記 - 清理 或者 標(biāo)記 - 整理 算法

垃圾收集器

以上是 HotSpot 虛擬機(jī)中的 7 個(gè)垃圾收集器,連線表示垃圾收集器可以配合使用。

1. Serial 收集器

Serial 翻譯為串行,可以理解為垃圾收集和用戶程序交替執(zhí)行,這意味著在執(zhí)行垃圾收集的時(shí)候需要停頓用戶程序。除了 CMS 和 G1 之外,其它收集器都是以串行的方式執(zhí)行。CMS 和 G1 可以使得垃圾收集和用戶程序同時(shí)執(zhí)行,被稱為并發(fā)執(zhí)行。

它是單線程的收集器,只會(huì)使用一個(gè)線程進(jìn)行垃圾收集工作。

它的優(yōu)點(diǎn)是簡單高效,對(duì)于單個(gè) CPU 環(huán)境來說,由于沒有線程交互的開銷,因此擁有最高的單線程收集效率。

它是 Client 模式下的默認(rèn)新生代收集器,因?yàn)樵谟脩舻淖烂鎽?yīng)用場景下,分配給虛擬機(jī)管理的內(nèi)存一般來說不會(huì)很大。Serial 收集器收集幾十兆甚至一兩百兆的新生代停頓時(shí)間可以控制在一百多毫秒以內(nèi),只要不是太頻繁,這點(diǎn)停頓是可以接受的。

2. ParNew 收集器

它是 Serial 收集器的多線程版本。

是 Server 模式下的虛擬機(jī)首選新生代收集器,除了性能原因外,主要是因?yàn)槌?Serial 收集器,只有它能與 CMS 收集器配合工作。

默認(rèn)開始的線程數(shù)量與 CPU 數(shù)量相同,可以使用 -XX:ParallelGCThreads 參數(shù)來設(shè)置線程數(shù)。

3. Parallel Scavenge 收集器

與 ParNew 一樣是并行的多線程收集器。

其它收集器關(guān)注點(diǎn)是盡可能縮短垃圾收集時(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ù)。

提供了兩個(gè)參數(shù)用于精確控制吞吐量,分別是控制最大垃圾收集停頓時(shí)間 -XX:MaxGCPauseMillis 參數(shù)以及直接設(shè)置吞吐量大小的 -XX:GCTimeRatio 參數(shù)(值為大于 0 且小于 100 的整數(shù))。縮短停頓時(shí)間是以犧牲吞吐量和新生代空間來換取的:新生代空間變小,垃圾回收變得頻繁,導(dǎo)致吞吐量下降。

還提供了一個(gè)參數(shù) -XX:+UseAdaptiveSizePolicy,這是一個(gè)開關(guān)參數(shù),打開參數(shù)后,就不需要手工指定新生代的大小(-Xmn)、Eden 和 Survivor 區(qū)的比例(-XX:SurvivorRatio)、晉升老年代對(duì)象年齡(-XX:PretenureSizeThreshold)等細(xì)節(jié)參數(shù)了,虛擬機(jī)會(huì)根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間或者最大的吞吐量,這種方式稱為 GC 自適應(yīng)的調(diào)節(jié)策略(GC Ergonomics)。

4. Serial Old 收集器

是 Serial 收集器的老年代版本,也是給 Client 模式下的虛擬機(jī)使用。如果用在 Server 模式下,它有兩大用途:

  • 在 JDK 1.5 以及之前版本(Parallel Old 誕生以前)中與 Parallel Scavenge 收集器搭配使用。

  • 作為 CMS 收集器的后備預(yù)案,在并發(fā)收集發(fā)生 Concurrent Mode Failure 時(shí)使用。

5. Parallel Old 收集器

是 Parallel Scavenge 收集器的老年代版本。

在注重吞吐量以及 CPU 資源敏感的場合,都可以優(yōu)先考慮 Parallel Scavenge 加 Parallel Old 收集器。

6. CMS 收集器

CMS(Concurrent Mark Sweep),Mark Sweep 指的是標(biāo)記 - 清除算法。

特點(diǎn):并發(fā)收集、低停頓。并發(fā)指的是用戶線程和 GC 線程同時(shí)運(yùn)行。

分為以下四個(gè)流程:

  • 初始標(biāo)記:僅僅只是標(biāo)記一下 GC Roots 能直接關(guān)聯(lián)到的對(duì)象,速度很快,需要停頓。

  • 并發(fā)標(biāo)記:進(jìn)行 GC Roots Tracing 的過程,它在整個(gè)回收過程中耗時(shí)最長,不需要停頓。

  • 重新標(biāo)記:為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,需要停頓。

  • 并發(fā)清除:不需要停頓。

在整個(gè)過程中耗時(shí)最長的并發(fā)標(biāo)記和并發(fā)清除過程中,收集器線程都可以與用戶線程一起工作,不需要進(jìn)行停頓。

具有以下缺點(diǎn):

  • 吞吐量低:低停頓時(shí)間是以犧牲吞吐量為代價(jià)的,導(dǎo)致 CPU 利用率不夠高。

  • 無法處理浮動(dòng)垃圾,可能出現(xiàn) Concurrent Mode Failure。浮動(dòng)垃圾是指并發(fā)清除階段由于用戶線程繼續(xù)運(yùn)行而產(chǎn)生的垃圾,這部分垃圾只能到下一次 GC 時(shí)才能進(jìn)行回收。由于浮動(dòng)垃圾的存在,因此需要預(yù)留出一部分內(nèi)存,意味著 CMS 收集不能像其它收集器那樣等待老年代快滿的時(shí)候再回收。可以使用 -XX:CMSInitiatingOccupancyFraction 來改變觸發(fā) CMS 收集器工作的內(nèi)存占用百分,如果這個(gè)值設(shè)置的太大,導(dǎo)致預(yù)留的內(nèi)存不夠存放浮動(dòng)垃圾,就會(huì)出現(xiàn) Concurrent Mode Failure,這時(shí)虛擬機(jī)將臨時(shí)啟用 Serial Old 來替代 CMS。

  • 標(biāo)記 - 清除算法導(dǎo)致的空間碎片,往往出現(xiàn)老年代空間剩余,但無法找到足夠大連續(xù)空間來分配當(dāng)前對(duì)象,不得不提前觸發(fā)一次 Full GC。

7. G1 收集器

G1(Garbage-First),它是一款面向服務(wù)端應(yīng)用的垃圾收集器,在多 CPU 和大內(nèi)存的場景下有很好的性能。HotSpot 開發(fā)團(tuán)隊(duì)賦予它的使命是未來可以替換掉 CMS 收集器。

Java 堆被分為新生代、老年代和永久代,其它收集器進(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è)步驟:

  • 初始標(biāo)記

  • 并發(fā)標(biāo)記

  • 最終標(biāo)記:為了修正在并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分標(biāo)記記錄,虛擬機(jī)將這段時(shí)間對(duì)象變化記錄在線程的 Remembered Set Logs 里面,最終標(biāo)記階段需要把 Remembered Set Logs 的數(shù)據(jù)合并到 Remembered Set 中。這階段需要停頓線程,但是可并行執(zhí)行。

  • 篩選回收:首先對(duì)各個(gè) Region 中的回收價(jià)值和成本進(jìn)行排序,根據(jù)用戶所期望的 GC 停頓是時(shí)間來制定回收計(jì)劃。此階段其實(shí)也可以做到與用戶程序一起并發(fā)執(zhí)行,但是因?yàn)橹换厥找徊糠?Region,時(shí)間是用戶可控制的,而且停頓用戶線程將大幅度提高收集效率。

具備如下特點(diǎn):

  • 空間整合:整體來看是基于“標(biāo)記 - 整理”算法實(shí)現(xiàn)的收集器,從局部(兩個(gè) Region 之間)上來看是基于“復(fù)制”算法實(shí)現(xiàn)的,這意味著運(yùn)行期間不會(huì)產(chǎn)生內(nèi)存空間碎片。

  • 可預(yù)測的停頓:能讓使用者明確指定在一個(gè)長度為 M 毫秒的時(shí)間片段內(nèi),消耗在 GC 上的時(shí)間不得超過 N 毫秒。

更詳細(xì)內(nèi)容請(qǐng)參考:Getting Started with the G1 Garbage Collector

8. 比較

內(nèi)存分配與回收策略

對(duì)象的內(nèi)存分配,也就是在堆上分配。主要分配在新生代的 Eden 區(qū)上,少數(shù)情況下也可能直接分配在老年代中。

1. Minor GC 和 Full GC

  • Minor GC:發(fā)生在新生代上,因?yàn)樾律鷮?duì)象存活時(shí)間很短,因此 Minor GC 會(huì)頻繁執(zhí)行,執(zhí)行的速度一般也會(huì)比較快。

  • Full GC:發(fā)生在老年代上,老年代對(duì)象和新生代的相反,其存活時(shí)間長,因此 Full GC 很少執(zhí)行,而且執(zhí)行速度會(huì)比 Minor GC 慢很多。

2. 內(nèi)存分配策略

(一)對(duì)象優(yōu)先在 Eden 分配

大多數(shù)情況下,對(duì)象在新生代 Eden 區(qū)分配,當(dāng) Eden 區(qū)空間不夠時(shí),發(fā)起 Minor GC。

(二)大對(duì)象直接進(jìn)入老年代

大對(duì)象是指需要連續(xù)內(nèi)存空間的對(duì)象,最典型的大對(duì)象是那種很長的字符串以及數(shù)組。

經(jīng)常出現(xiàn)大對(duì)象會(huì)提前觸發(fā)垃圾收集以獲取足夠的連續(xù)空間分配給大對(duì)象。

-XX:PretenureSizeThreshold,大于此值的對(duì)象直接在老年代分配,避免在 Eden 區(qū)和 Survivor 區(qū)之間的大量內(nèi)存復(fù)制。

(三)長期存活的對(duì)象進(jìn)入老年代

為對(duì)象定義年齡計(jì)數(shù)器,對(duì)象在 Eden 出生并經(jīng)過 Minor GC 依然存活,將移動(dòng)到 Survivor 中,年齡就增加 1 歲,增加到一定年齡則移動(dòng)到老年代中。

-XX:MaxTenuringThreshold 用來定義年齡的閾值。

(四)動(dòng)態(tài)對(duì)象年齡判定

虛擬機(jī)并不是永遠(yuǎn)地要求對(duì)象的年齡必須達(dá)到 MaxTenuringThreshold 才能晉升老年代,如果在 Survivor 區(qū)中相同年齡所有對(duì)象大小的總和大于 Survivor 空間的一半,則年齡大于或等于該年齡的對(duì)象可以直接進(jìn)入老年代,無需等到 MaxTenuringThreshold 中要求的年齡。

(五)空間分配擔(dān)保

在發(fā)生 Minor GC 之前,虛擬機(jī)先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對(duì)象總空間,如果條件成立的話,那么 Minor GC 可以確認(rèn)是安全的;如果不成立的話虛擬機(jī)會(huì)查看 HandlePromotionFailure 設(shè)置值是否允許擔(dān)保失敗,如果允許那么就會(huì)繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對(duì)象的平均大小,如果大于,將嘗試著進(jìn)行一次 Minor GC,盡管這次 Minor GC 是有風(fēng)險(xiǎn)的;如果小于,或者 HandlePromotionFailure 設(shè)置不允許冒險(xiǎn),那這時(shí)也要改為進(jìn)行一次 Full GC。

3. Full GC 的觸發(fā)條件

對(duì)于 Minor GC,其觸發(fā)條件非常簡單,當(dāng) Eden 區(qū)空間滿時(shí),就將觸發(fā)一次 Minor GC。而 Full GC 則相對(duì)復(fù)雜,有以下條件:

(一)調(diào)用 System.gc()

此方法的調(diào)用是建議虛擬機(jī)進(jìn)行 Full GC,雖然只是建議而非一定,但很多情況下它會(huì)觸發(fā) Full GC,從而增加 Full GC 的頻率,也即增加了間歇性停頓的次數(shù)。因此強(qiáng)烈建議能不使用此方法就不要使用,讓虛擬機(jī)自己去管理它的內(nèi)存。可通過 -XX:DisableExplicitGC 來禁止 RMI 調(diào)用 System.gc()。

(二)老年代空間不足

老年代空間不足的常見場景為前文所講的大對(duì)象直接進(jìn)入老年代、長期存活的對(duì)象進(jìn)入老年代等,當(dāng)執(zhí)行 Full GC 后空間仍然不足,則拋出 Java.lang.OutOfMemoryError。為避免以上原因引起的 Full GC,調(diào)優(yōu)時(shí)應(yīng)盡量做到讓對(duì)象在 Minor GC 階段被回收、讓對(duì)象在新生代多存活一段時(shí)間以及不要?jiǎng)?chuàng)建過大的對(duì)象及數(shù)組。

(三)空間分配擔(dān)保失敗

使用復(fù)制算法的 Minor GC 需要老年代的內(nèi)存空間作擔(dān)保,如果出現(xiàn)了 HandlePromotionFailure 擔(dān)保失敗,則會(huì)觸發(fā) Full GC。

(四)JDK 1.7 及以前的永久代空間不足

在 JDK 1.7 及以前,HotSpot 虛擬機(jī)中的方法區(qū)是用永久代實(shí)現(xiàn)的,永久代中存放的為一些 Class 的信息、常量、靜態(tài)變量等數(shù)據(jù),當(dāng)系統(tǒng)中要加載的類、反射的類和調(diào)用的方法較多時(shí),永久代可能會(huì)被占滿,在未配置為采用 CMS GC 的情況下也會(huì)執(zhí)行 Full GC。如果經(jīng)過 Full GC 仍然回收不了,那么虛擬機(jī)會(huì)拋出 java.lang.OutOfMemoryError,為避免以上原因引起的 Full GC,可采用的方法為增大永久代空間或轉(zhuǎn)為使用 CMS GC。

(五)Concurrent Mode Failure

執(zhí)行 CMS GC 的過程中同時(shí)有對(duì)象要放入老年代,而此時(shí)老年代空間不足(有時(shí)候“空間不足”是 CMS GC 時(shí)當(dāng)前的浮動(dòng)垃圾過多導(dǎo)致暫時(shí)性的空間不足觸發(fā) Full GC),便會(huì)報(bào) Concurrent Mode Failure 錯(cuò)誤,并觸發(fā) Full GC。

免費(fèi)Java資料需要自己領(lǐng)取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并發(fā)分布式等教程,一共30G。?
傳送門:?https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q


轉(zhuǎn)載于:https://blog.51cto.com/13672983/2379075

總結(jié)

以上是生活随笔為你收集整理的深入理解JVM垃圾收集机制,下次面试你准备好了吗的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。