java中GC

GC 垃圾收集 Garbage Collection

GC主要做了清理對(duì)象,整理內(nèi)存的工作。Java堆分為新生代和老年代炼邀,采用了不同的回收方式。
Java中一個(gè)接口的多個(gè)實(shí)現(xiàn)類所需要的內(nèi)存可能不一樣剪侮,一個(gè)方法中的多個(gè)分支需要的內(nèi)存也可能不一樣拭宁,我們只有在程序處于運(yùn)行期間時(shí)才會(huì)知道創(chuàng)建了哪些對(duì)象,這部分內(nèi)存的分配時(shí)動(dòng)態(tài)的瓣俯,而程序計(jì)數(shù)器杰标、虛擬機(jī)棧、本地方法棧這幾個(gè)區(qū)域內(nèi)存的分配和回收都是確定的彩匕,因此垃圾收集器關(guān)注的就是Java堆腔剂。

正式閱讀之前需要了解相關(guān)概念:

JVM區(qū)域總體分兩類,heap區(qū)和非heap區(qū)驼仪。
heap區(qū)又分為:
Eden Space(伊甸園)掸犬、
Survivor Space(幸存者區(qū))、
Old Gen(老年代)绪爸。
非heap區(qū)又分:
Code Cache(代碼緩存區(qū))湾碎;
Perm Gen(永久代);
Jvm Stack(java虛擬機(jī)棧)奠货;
Local Method Statck(本地方法棧)介褥;

Java 堆內(nèi)存分為新生代和老年代,新生代中又分為1個(gè) Eden(對(duì)象被創(chuàng)建的時(shí)候首先放到這個(gè)區(qū)域递惋,進(jìn)行垃圾回收后柔滔,不能被回收的對(duì)象被放入到空的survivor區(qū)域。) 區(qū)域 和 2個(gè) Survivor(用于保存在eden space內(nèi)存區(qū)域中經(jīng)過垃圾回收后沒有被回收的對(duì)象) 區(qū)域丹墨。

  • 年輕代(Young Gen):
    Eden Space字面意思是伊甸園廊遍,對(duì)象被創(chuàng)建的時(shí)候首先放到這個(gè)區(qū)域,進(jìn)行垃圾回收后贩挣,不能被回收的對(duì)象被放入到空的survivor區(qū)域喉前。
    Survivor Space幸存者區(qū),用于保存在eden space內(nèi)存區(qū)域中經(jīng)過垃圾回收后沒有被回收的對(duì)象王财。Survivor有兩個(gè)卵迂,分別為To Survivor、 From Survivor绒净,這個(gè)兩個(gè)區(qū)域的空間大小是一樣的见咒。執(zhí)行垃圾回收的時(shí)候Eden區(qū)域不能被回收的對(duì)象被放入到空的survivor(也就是To Survivor,同時(shí)Eden區(qū)域的內(nèi)存會(huì)在垃圾回收的過程中全部釋放)挂疆,另一個(gè)survivor(即From Survivor)里不能被回收的對(duì)象也會(huì)被放入這個(gè)survivor(即To Survivor)改览,然后To Survivor 和 From Survivor的標(biāo)記會(huì)互換下翎,始終保證一個(gè)survivor是空的。
    有關(guān)年輕代的JVM參數(shù)
用于設(shè)置年輕代的大小宝当,建議設(shè)為整個(gè)堆大小的1/3或者1/4,兩個(gè)值設(shè)為一樣大视事。
-XX:NewSize和-XX:MaxNewSize
用于設(shè)置Eden和其中一個(gè)Survivor的比值,這個(gè)值也比較重要庆揩。
-XX:SurvivorRatio
這個(gè)參數(shù)用于顯示每次Minor GC時(shí)Survivor區(qū)中各個(gè)年齡段的對(duì)象的大小俐东。
-XX:+PrintTenuringDistribution
用于設(shè)置晉升到老年代的對(duì)象年齡的最小值和最大值,每個(gè)對(duì)象在堅(jiān)持過一次Minor GC之后订晌,年齡就加1虏辫。
-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold
  • 年老代(Tenured Gen):
    年老代主要存放JVM認(rèn)為生命周期比較長(zhǎng)的對(duì)象(經(jīng)過幾次的Young Gen的垃圾回收后仍然存在),內(nèi)存大小相對(duì)會(huì)比較大锈拨,垃圾回收也相對(duì)沒有那么頻繁(譬如可能幾個(gè)小時(shí)一次)砌庄。年老代主要采用壓縮的方式來避免內(nèi)存碎片(將存活對(duì)象移動(dòng)到內(nèi)存片的一邊,也就是內(nèi)存整理)推励。當(dāng)然鹤耍,有些垃圾回收器(譬如CMS垃圾回收器)出于效率的原因,可能會(huì)不進(jìn)行壓縮验辞。
  • 持久代(Perm Gen):
    Perm Gen全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域喊衫,因而稱之為永久代跌造。這個(gè)內(nèi)存區(qū)域用于存放Class和Meta的信息,Class在被 Load的時(shí)候被放入這個(gè)區(qū)域族购。因?yàn)镻erm里存儲(chǔ)的東西永遠(yuǎn)不會(huì)被JVM垃圾回收的壳贪,所以如果你的應(yīng)用程序LOAD很多CLASS的話,就很可能出現(xiàn)PermGen space錯(cuò)誤寝杖。默認(rèn)大小為物理內(nèi)存的1/64违施。
  • Code Cache代碼緩存區(qū),它主要用于存放JIT所編譯的代碼瑟幕。CodeCache代碼緩沖區(qū)的大小在client模式下默認(rèn)最大是32m磕蒲,在server模式下默認(rèn)是48m,這個(gè)值也是可以設(shè)置的只盹,它所對(duì)應(yīng)的JVM參數(shù)為ReservedCodeCacheSize 和 InitialCodeCacheSize辣往,可以通過如下的方式來為Java程序設(shè)置。
-XX:ReservedCodeCacheSize=128m

CodeCache緩存區(qū)是可能被充滿的殖卑,當(dāng)CodeCache滿時(shí)站削,后臺(tái)會(huì)收到CodeCache is full的警告信息,如下所示:
“CompilerThread0” java.lang.OutOfMemoryError: requested 2854248 bytes for Chunk::new. Out of swap space?

注:JIT編譯器是在程序運(yùn)行期間孵稽,將Java字節(jié)碼編譯成平臺(tái)相關(guān)的二進(jìn)制代碼许起。正因?yàn)榇司幾g行為發(fā)生在程序運(yùn)行期間十偶,所以該編譯器被稱為Just-In-Time編譯器。

需要GC的內(nèi)存區(qū)域

jvm 中园细,程序計(jì)數(shù)器惦积、虛擬機(jī)棧、本地方法棧都是隨線程而生隨線程而滅珊肃,棧幀隨著方法的進(jìn)入和退出做入棧和出棧操作荣刑,實(shí)現(xiàn)了自動(dòng)的內(nèi)存清理,因此伦乔,我們的內(nèi)存垃圾回收主要集中于 java 堆和方法區(qū)中厉亏,在程序運(yùn)行期間,這部分內(nèi)存的分配和使用都是動(dòng)態(tài)的烈和。

image.png

判斷對(duì)象是否存活

需要進(jìn)行回收的對(duì)象就是已經(jīng)沒有存活的對(duì)象爱只,判斷一個(gè)對(duì)象是否存活常用的有兩種辦法:引用計(jì)數(shù)和可達(dá)分析。

(1)引用計(jì)數(shù):每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性招刹,新增一個(gè)引用時(shí)計(jì)數(shù)加1恬试,引用釋放時(shí)計(jì)數(shù)減1,計(jì)數(shù)為0時(shí)可以回收疯暑。此方法簡(jiǎn)單训柴,無法解決對(duì)象相互循環(huán)引用的問題。

    A a=new A();
    B b=new B();
    a.object=B;
    b.object=A;
    a=null;
    b=null;

實(shí)際上這兩個(gè)對(duì)象都不可能再被訪問妇拯,但是因?yàn)樗鼈兓ハ嘁脤?dǎo)致引用計(jì)數(shù)不為0無法被回收幻馁。為了解決這個(gè)問題,Java采用下面的可達(dá)性分析算法越锈。
(2)可達(dá)性分析(Reachability Analysis):從GC Roots開始向下搜索仗嗦,搜索所走過的路徑稱為引用鏈。當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí)甘凭,則證明此對(duì)象是不可用的稀拐。不可達(dá)對(duì)象。

在Java語言中丹弱,GC Roots包括:
虛擬機(jī)棧中引用的對(duì)象德撬。
方法區(qū)中類靜態(tài)屬性實(shí)體引用的對(duì)象。
方法區(qū)中常量引用的對(duì)象蹈矮。
本地方法棧中JNI引用的對(duì)象砰逻。

引用

如果reference類型的數(shù)據(jù)中存儲(chǔ)的數(shù)值代表的是另外一塊內(nèi)存的起始地址,就稱這塊內(nèi)存代表著一個(gè)引用泛鸟。
這個(gè)定義過于簡(jiǎn)單以至于沒有實(shí)用價(jià)值蝠咆。 JDK1.2之后對(duì)引用的概念進(jìn)行了擴(kuò)充。
1.強(qiáng)引用:在程序代碼中普遍存在的類似“Object obj = new Object()”這類的引用,只要強(qiáng)引用還在刚操,垃圾回收器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象闸翅。
2.軟引用 :描述還有用但并非必須的對(duì)象。僅在系統(tǒng)內(nèi)存不足時(shí)進(jìn)行清理菊霜。對(duì)應(yīng)的類是SoftReference
3.弱引用:描述非必需對(duì)象坚冀。無論內(nèi)存是否足夠都會(huì)被清理。對(duì)應(yīng)的類時(shí)WeakReference
4.虛引用:最弱的引用關(guān)系鉴逞,一個(gè)對(duì)象是否有虛引用的存在完全不會(huì)對(duì)其生存時(shí)間造成影響记某,也無法通過虛引用來取得一個(gè)對(duì)象實(shí)例,為對(duì)象設(shè)置虛引用的唯一目的是能在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知构捡。對(duì)應(yīng)的類是PhantomReference
引用強(qiáng)度:強(qiáng)引用》軟引用》弱引用》虛引用

什么時(shí)候觸發(fā)GC

(1)程序調(diào)用System.gc時(shí)可以觸發(fā)

(2)系統(tǒng)自身來決定GC觸發(fā)的時(shí)機(jī)(根據(jù)Eden區(qū)和From Space區(qū)的內(nèi)存大小來決定液南。當(dāng)內(nèi)存大小不足時(shí),則會(huì)啟動(dòng)GC線程并停止應(yīng)用線程)

GC又分為 minor GC 和 Full GC (也稱為 Major GC )
Minor GC觸發(fā)條件:當(dāng)Eden區(qū)滿時(shí),觸發(fā)Minor GC。
Full GC觸發(fā)條件:
a.調(diào)用System.gc時(shí)吨凑,系統(tǒng)建議執(zhí)行Full GC,但是不必然執(zhí)行
b.老年代空間不足
c.方法去空間不足
d.通過Minor GC后進(jìn)入老年代的平均大小大于老年代的可用內(nèi)存
e.由Eden區(qū)畅姊、From Space區(qū)向To Space區(qū)復(fù)制時(shí),對(duì)象大小大于To Space可用內(nèi)存吹由,則把該對(duì)象轉(zhuǎn)存到老年代若未,且老年代的可用內(nèi)存小于該對(duì)象大小

GC常用算法

GC常用算法有:標(biāo)記-清除算法,標(biāo)記-壓縮算法倾鲫,復(fù)制算法陨瘩,分代收集算法。
目前主流的JVM(HotSpot)采用的是分代收集算法级乍。

標(biāo)記-清除算法

為每個(gè)對(duì)象存儲(chǔ)一個(gè)標(biāo)記位,記錄對(duì)象的狀態(tài)(活著或是死亡)帚湘。分為兩個(gè)階段玫荣,一個(gè)是標(biāo)記階段,這個(gè)階段內(nèi)大诸,為每個(gè)對(duì)象更新標(biāo)記位捅厂,檢查對(duì)象是否死亡;第二個(gè)階段是清除階段资柔,該階段對(duì)死亡的對(duì)象進(jìn)行清除焙贷,執(zhí)行 GC 操作。

優(yōu)點(diǎn)
最大的優(yōu)點(diǎn)是贿堰,標(biāo)記—清除算法中每個(gè)活著的對(duì)象的引用只需要找到一個(gè)即可辙芍,找到一個(gè)就可以判斷它為活的。此外,更重要的是故硅,這個(gè)算法并不移動(dòng)對(duì)象的位置庶灿。

缺點(diǎn)
它的缺點(diǎn)就是效率比較低(遞歸與全堆對(duì)象遍歷)。每個(gè)活著的對(duì)象都要在標(biāo)記階段遍歷一遍吃衅;所有對(duì)象都要在清除階段掃描一遍往踢,因此算法復(fù)雜度較高。沒有移動(dòng)對(duì)象徘层,導(dǎo)致可能出現(xiàn)很多碎片空間無法利用的情況峻呕。


image.png
標(biāo)記-壓縮算法(標(biāo)記-整理)

標(biāo)記-壓縮法是標(biāo)記-清除法的一個(gè)改進(jìn)版。同樣趣效,在標(biāo)記階段瘦癌,該算法也將所有對(duì)象標(biāo)記為存活和死亡兩種狀態(tài);不同的是英支,在第二個(gè)階段佩憾,該算法并沒有直接對(duì)死亡的對(duì)象進(jìn)行清理,而是將所有存活的對(duì)象整理一下干花,放到另一處空間妄帘,然后把剩下的所有對(duì)象全部清除。這樣就達(dá)到了標(biāo)記-整理的目的池凄。

優(yōu)點(diǎn)
該算法不會(huì)像標(biāo)記-清除算法那樣產(chǎn)生大量的碎片空間抡驼。
缺點(diǎn)
如果存活的對(duì)象過多,整理階段將會(huì)執(zhí)行較多復(fù)制操作肿仑,導(dǎo)致算法效率降低致盟。

image.png
復(fù)制算法

該算法將內(nèi)存平均分成兩部分,然后每次只使用其中的一部分尤慰,當(dāng)這部分內(nèi)存滿的時(shí)候馏锡,將內(nèi)存中所有存活的對(duì)象復(fù)制到另一個(gè)內(nèi)存中,然后將之前的內(nèi)存清空伟端,只使用這部分內(nèi)存杯道,循環(huán)下去。

注意:
這個(gè)算法與標(biāo)記-整理算法的區(qū)別在于责蝠,該算法不是在同一個(gè)區(qū)域復(fù)制党巾,而是將所有存活的對(duì)象復(fù)制到另一個(gè)區(qū)域內(nèi)。

優(yōu)點(diǎn)
實(shí)現(xiàn)簡(jiǎn)單霜医;不產(chǎn)生內(nèi)存碎片

缺點(diǎn)
每次運(yùn)行齿拂,總有一半內(nèi)存是空的,導(dǎo)致可使用的內(nèi)存空間只有原來的一半肴敛。


image.png
分代收集算法

現(xiàn)在的虛擬機(jī)垃圾收集大多采用這種方式署海,它根據(jù)對(duì)象的生存周期,將堆分為新生代(Young)和老年代(Tenure)。在新生代中叹侄,由于對(duì)象生存期短巩搏,每次回收都會(huì)有大量對(duì)象死去,那么這時(shí)就采用復(fù)制算法趾代。老年代里的對(duì)象存活率較高贯底,沒有額外的空間進(jìn)行分配擔(dān)保,所以可以使用標(biāo)記-整理 或者 標(biāo)記-清除撒强。

具體過程:新生代(Young)分為Eden區(qū)禽捆,F(xiàn)rom區(qū)與To區(qū)


image.png

當(dāng)系統(tǒng)創(chuàng)建一個(gè)對(duì)象的時(shí)候,總是在Eden區(qū)操作飘哨,當(dāng)這個(gè)區(qū)滿了胚想,那么就會(huì)觸發(fā)一次YoungGC,也就是年輕代的垃圾回收芽隆。一般來說這時(shí)候不是所有的對(duì)象都沒用了浊服,所以就會(huì)把還能用的對(duì)象復(fù)制到From區(qū)。


image.png

這樣整個(gè)Eden區(qū)就被清理干凈了胚吁,可以繼續(xù)創(chuàng)建新的對(duì)象牙躺,當(dāng)Eden區(qū)再次被用完,就再觸發(fā)一次YoungGC腕扶,然后呢孽拷,注意,這個(gè)時(shí)候跟剛才稍稍有點(diǎn)區(qū)別半抱。這次觸發(fā)YoungGC后脓恕,會(huì)將Eden區(qū)與From區(qū)還在被使用的對(duì)象復(fù)制到To區(qū)。
image.png

再下一次YoungGC的時(shí)候窿侈,則是將Eden區(qū)與To區(qū)中的還在被使用的對(duì)象復(fù)制到From區(qū)炼幔。


image.png

經(jīng)過若干次YoungGC后,有些對(duì)象在From與To之間來回游蕩史简,這時(shí)候From區(qū)與To區(qū)亮出了底線(閾值)江掩,這些家伙要是到現(xiàn)在還沒掛掉,對(duì)不起乘瓤,一起滾到(復(fù)制)老年代吧。
image.png

老年代經(jīng)過這么幾次折騰策泣,也就扛不住了(空間被用完)衙傀,好,那就來次集體大掃除(Full GC)萨咕,也就是全量回收统抬。如果Full GC使用太頻繁的話,無疑會(huì)對(duì)系統(tǒng)性能產(chǎn)生很大的影響。所以要合理設(shè)置年輕代與老年代的大小聪建,盡量減少Full GC的操作钙畔。

垃圾收集器

如果說收集算法是內(nèi)存回收的方法論,垃圾收集器就是內(nèi)存回收的具體實(shí)現(xiàn)金麸。

  • Serial收集器
    串行收集器是最古老擎析,最穩(wěn)定以及效率高的收集器
    可能會(huì)產(chǎn)生較長(zhǎng)的停頓,只使用一個(gè)線程去回收

-XX:+UseSerialGC
新生代挥下、老年代使用串行回收
新生代復(fù)制算法
老年代標(biāo)記-壓縮

image.png
  • 并行收集器
    1.ParNew
    -XX:+UseParNewGC(new代表新生代揍魂,所以適用于新生代)

新生代并行
老年代串行

Serial收集器新生代的并行版本
在新生代回收時(shí)使用復(fù)制算法
多線程,需要多核支持
-XX:ParallelGCThreads 限制線程數(shù)量

image.png

2.Parallel
類似ParNew
新生代復(fù)制算法
老年代標(biāo)記-壓縮
更加關(guān)注吞吐量

-XX:+UseParallelGC
使用Parallel收集器+ 老年代串行

-XX:+UseParallelOldGC
使用Parallel收集器+ 老年代并行

image.png

3.其他GC參數(shù)

-XX:MaxGCPauseMills
最大停頓時(shí)間棚瘟,單位毫秒
GC盡力保證回收時(shí)間不超過設(shè)定值

-XX:GCTimeRatio
0-100的取值范圍
垃圾收集時(shí)間占總時(shí)間的比
默認(rèn)99现斋,即最大允許1%時(shí)間做GC
這兩個(gè)參數(shù)是矛盾的。因?yàn)橥nD時(shí)間和吞吐量不可能同時(shí)調(diào)優(yōu)

  • CMS收集器

Concurrent Mark Sweep 并發(fā)標(biāo)記清除(應(yīng)用程序線程和GC線程交替執(zhí)行)
使用標(biāo)記-清除算法
并發(fā)階段會(huì)降低吞吐量(停頓時(shí)間減少偎蘸,吞吐量降低)
老年代收集器(新生代使用ParNew)
-XX:+UseConcMarkSweepGC

CMS運(yùn)行過程比較復(fù)雜庄蹋,著重實(shí)現(xiàn)了標(biāo)記的過程,可分為

  1. 初始標(biāo)記(會(huì)產(chǎn)生全局停頓)
    根可以直接關(guān)聯(lián)到的對(duì)象
    速度快

  2. 并發(fā)標(biāo)記(和用戶線程一起)
    主要標(biāo)記過程迷雪,標(biāo)記全部對(duì)象

  3. 重新標(biāo)記 (會(huì)產(chǎn)生全局停頓)
    由于并發(fā)標(biāo)記時(shí)限书,用戶線程依然運(yùn)行,因此在正式清理前振乏,再做修正蔗包。

  4. 并發(fā)清除(和用戶線程一起)
    基于標(biāo)記結(jié)果,直接清理對(duì)象


    image.png

這里就能很明顯的看出慧邮,為什么CMS要使用標(biāo)記清除而不是標(biāo)記壓縮调限,如果使用標(biāo)記壓縮,需要多對(duì)象的內(nèi)存位置進(jìn)行改變误澳,這樣程序就很難繼續(xù)執(zhí)行耻矮。但是標(biāo)記清除會(huì)產(chǎn)生大量?jī)?nèi)存碎片,不利于內(nèi)存分配忆谓。

CMS收集器特點(diǎn):
盡可能降低停頓
會(huì)影響系統(tǒng)整體吞吐量和性能
比如裆装,在用戶線程運(yùn)行過程中,分一半CPU去做GC倡缠,系統(tǒng)性能在GC階段哨免,反應(yīng)速度就下降一半
清理不徹底
因?yàn)樵谇謇黼A段,用戶線程還在運(yùn)行昙沦,會(huì)產(chǎn)生新的垃圾琢唾,無法清理
因?yàn)楹陀脩艟€程一起運(yùn)行,不能在空間快滿時(shí)再清理(因?yàn)橐苍S在并發(fā)GC的期間盾饮,用戶線程又申請(qǐng)了大量?jī)?nèi)存采桃,導(dǎo)致內(nèi)存不夠)

-XX:CMSInitiatingOccupancyFraction設(shè)置觸發(fā)GC的閾值
如果不幸內(nèi)存預(yù)留空間不夠懒熙,就會(huì)引起concurrent mode failure
一旦 concurrent mode failure產(chǎn)生,將使用串行收集器作為后備普办。

CMS也提供了整理碎片的參數(shù):
-XX:+ UseCMSCompactAtFullCollection Full GC后工扎,進(jìn)行一次整理

整理過程是獨(dú)占的,會(huì)引起停頓時(shí)間變長(zhǎng)
-XX:+CMSFullGCsBeforeCompaction

設(shè)置進(jìn)行幾次Full GC后衔蹲,進(jìn)行一次碎片整理
-XX:ParallelCMSThreads

設(shè)定CMS的線程數(shù)量(一般情況約等于可用CPU數(shù)量)
CMS的提出是想改善GC的停頓時(shí)間肢娘,在GC過程中的確做到了減少GC時(shí)間,但是同樣導(dǎo)致產(chǎn)生大量?jī)?nèi)存碎片踪危,又需要消耗大量時(shí)間去整理碎片蔬浙,從本質(zhì)上并沒有改善時(shí)間。

  • G1收集器

G1是目前技術(shù)發(fā)展的最前沿成果之一贞远,HotSpot開發(fā)團(tuán)隊(duì)賦予它的使命是未來可以替換掉JDK1.5中發(fā)布的CMS收集器畴博。

與CMS收集器相比G1收集器有以下特點(diǎn):

(1) 空間整合,G1收集器采用標(biāo)記整理算法蓝仲,不會(huì)產(chǎn)生內(nèi)存空間碎片俱病。分配大對(duì)象時(shí)不會(huì)因?yàn)闊o法找到連續(xù)空間而提前觸發(fā)下一次GC。

(2)可預(yù)測(cè)停頓袱结,這是G1的另一大優(yōu)勢(shì)亮隙,降低停頓時(shí)間是G1和CMS的共同關(guān)注點(diǎn),但G1除了追求低停頓外垢夹,還能建立可預(yù)測(cè)的停頓時(shí)間模型溢吻,能讓使用者明確指定在一個(gè)長(zhǎng)度為N毫秒的時(shí)間片段內(nèi),消耗在垃圾收集上的時(shí)間不得超過N毫秒果元,這幾乎已經(jīng)是實(shí)時(shí)Java(RTSJ)的垃圾收集器的特征了促王。

上面提到的垃圾收集器,收集的范圍都是整個(gè)新生代或者老年代而晒,而G1不再是這樣蝇狼。使用G1收集器時(shí),Java堆的內(nèi)存布局與其他收集器有很大差別倡怎,它將整個(gè)Java堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region)迅耘,雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔閡了监署,它們都是一部分(可以不連續(xù))Region的集合颤专。

G1的新生代收集跟ParNew類似,當(dāng)新生代占用達(dá)到一定比例的時(shí)候钠乏,開始出發(fā)收集血公。

和CMS類似,G1收集器收集老年代對(duì)象會(huì)有短暫停頓缓熟。

步驟:

(1)標(biāo)記階段累魔,首先初始標(biāo)記(Initial-Mark),這個(gè)階段是停頓的(Stop the World Event),并且會(huì)觸發(fā)一次普通Mintor GC够滑。對(duì)應(yīng)GC log:GC pause (young) (inital-mark)

(2)Root Region Scanning垦写,程序運(yùn)行過程中會(huì)回收survivor區(qū)(存活到老年代),這一過程必須在young GC之前完成彰触。

(3)Concurrent Marking梯投,在整個(gè)堆中進(jìn)行并發(fā)標(biāo)記(和應(yīng)用程序并發(fā)執(zhí)行),此過程可能被young GC中斷况毅。在并發(fā)標(biāo)記階段分蓖,若發(fā)現(xiàn)區(qū)域?qū)ο笾械乃袑?duì)象都是垃圾,那個(gè)這個(gè)區(qū)域會(huì)被立即回收(圖中打X)尔许。同時(shí)么鹤,并發(fā)標(biāo)記過程中,會(huì)計(jì)算每個(gè)區(qū)域的對(duì)象活性(區(qū)域中存活對(duì)象的比例)味廊。


image.png

(4)Remark, 再標(biāo)記蒸甜,會(huì)有短暫停頓(STW)。再標(biāo)記階段是用來收集 并發(fā)標(biāo)記階段 產(chǎn)生新的垃圾(并發(fā)階段和應(yīng)用程序一同運(yùn)行)余佛;G1中采用了比CMS更快的初始快照算法:snapshot-at-the-beginning (SATB)柠新。

(5)Copy/Clean up,多線程清除失活對(duì)象辉巡,會(huì)有STW恨憎。G1將回收區(qū)域的存活對(duì)象拷貝到新區(qū)域,清除Remember Sets郊楣,并發(fā)清空回收區(qū)域并把它返回到空閑區(qū)域鏈表中憔恳。


image.png

(6)復(fù)制/清除過程后×「剩回收區(qū)域的活性對(duì)象已經(jīng)被集中回收到深藍(lán)色和深綠色區(qū)域喇嘱。

finalize()方法詳解

  1. finalize的作用

(1)finalize()是Object的protected方法,子類可以覆蓋該方法以實(shí)現(xiàn)資源清理工作塞栅,GC在回收對(duì)象之前調(diào)用該方法者铜。
(2)finalize()與C++中的析構(gòu)函數(shù)不是對(duì)應(yīng)的。C++中的析構(gòu)函數(shù)調(diào)用的時(shí)機(jī)是確定的(對(duì)象離開作用域或delete掉)放椰,但Java中的finalize的調(diào)用具有不確定性
(3)不建議用finalize方法完成“非內(nèi)存資源”的清理工作作烟,但建議用于:① 清理本地對(duì)象(通過JNI創(chuàng)建的對(duì)象);② 作為確保某些非內(nèi)存資源(如Socket砾医、文件等)釋放的一個(gè)補(bǔ)充:在finalize方法中顯式調(diào)用其他資源釋放方法拿撩。其原因可見下文[finalize的問題]

  1. finalize的問題
    (1)一些與finalize相關(guān)的方法,由于一些致命的缺陷如蚜,已經(jīng)被廢棄了压恒,如System.runFinalizersOnExit()方法影暴、Runtime.runFinalizersOnExit()方法
    (2)System.gc()與System.runFinalization()方法增加了finalize方法執(zhí)行的機(jī)會(huì),但不可盲目依賴它們
    (3)Java語言規(guī)范并不保證finalize方法會(huì)被及時(shí)地執(zhí)行探赫、而且根本不會(huì)保證它們會(huì)被執(zhí)行
    (4)finalize方法可能會(huì)帶來性能問題型宙。因?yàn)镴VM通常在單獨(dú)的低優(yōu)先級(jí)線程中完成finalize的執(zhí)行
    (5)對(duì)象再生問題:finalize方法中,可將待回收對(duì)象賦值給GC Roots可達(dá)的對(duì)象引用伦吠,從而達(dá)到對(duì)象再生的目的
    (6)finalize方法至多由GC執(zhí)行一次(用戶當(dāng)然可以手動(dòng)調(diào)用對(duì)象的finalize方法妆兑,但并不影響GC對(duì)finalize的行為)

  2. finalize的執(zhí)行過程(生命周期)

(1) 首先,大致描述一下finalize流程:當(dāng)對(duì)象變成(GC Roots)不可達(dá)時(shí)毛仪,GC會(huì)判斷該對(duì)象是否覆蓋了finalize方法搁嗓,若未覆蓋,則直接將其回收箱靴。否則腺逛,若對(duì)象未執(zhí)行過finalize方法,將其放入F-Queue隊(duì)列刨晴,由一低優(yōu)先級(jí)線程執(zhí)行該隊(duì)列中對(duì)象的finalize方法屉来。執(zhí)行finalize方法完畢后,GC會(huì)再次判斷該對(duì)象是否可達(dá)狈癞,若不可達(dá)茄靠,則進(jìn)行回收,否則蝶桶,對(duì)象“復(fù)活”慨绳。
(2) 具體的finalize流程:
對(duì)象可由兩種狀態(tài),涉及到兩類狀態(tài)空間真竖,一是終結(jié)狀態(tài)空間 F = {unfinalized, finalizable, finalized}脐雪;二是可達(dá)狀態(tài)空間 R = {reachable, finalizer-reachable, unreachable}。各狀態(tài)含義如下:

unfinalized: 新建對(duì)象會(huì)先進(jìn)入此狀態(tài)恢共,GC并未準(zhǔn)備執(zhí)行其finalize方法战秋,因?yàn)樵搶?duì)象是可達(dá)的
finalizable: 表示GC可對(duì)該對(duì)象執(zhí)行finalize方法,GC已檢測(cè)到該對(duì)象不可達(dá)讨韭。正如前面所述脂信,GC通過F-Queue隊(duì)列和一專用線程完成finalize的執(zhí)行
finalized: 表示GC已經(jīng)對(duì)該對(duì)象執(zhí)行過finalize方法
reachable: 表示GC Roots引用可達(dá)
finalizer-reachable(f-reachable):表示不是reachable,但可通過某個(gè)finalizable對(duì)象可達(dá)
unreachable:對(duì)象不可通過上面兩種途徑可達(dá)

image.png

變遷說明:

(1)新建對(duì)象首先處于[reachable, unfinalized]狀態(tài)(A)
(2)隨著程序的運(yùn)行透硝,一些引用關(guān)系會(huì)消失狰闪,導(dǎo)致狀態(tài)變遷,從reachable狀態(tài)變遷到f-reachable(B, C, D)或unreachable(E, F)狀態(tài)
(3)若JVM檢測(cè)到處于unfinalized狀態(tài)的對(duì)象變成f-reachable或unreachable濒生,JVM會(huì)將其標(biāo)記為finalizable狀態(tài)(G,H)埋泵。若對(duì)象原處于[unreachable, unfinalized]狀態(tài),則同時(shí)將其標(biāo)記為f-reachable(H)。
(4)在某個(gè)時(shí)刻丽声,JVM取出某個(gè)finalizable對(duì)象礁蔗,將其標(biāo)記為finalized并在某個(gè)線程中執(zhí)行其finalize方法。由于是在活動(dòng)線程中引用了該對(duì)象雁社,該對(duì)象將變遷到(reachable, finalized)狀態(tài)(K或J)瘦麸。該動(dòng)作將影響某些其他對(duì)象從f-reachable狀態(tài)重新回到reachable狀態(tài)(L, M, N)
(5)處于finalizable狀態(tài)的對(duì)象不能同時(shí)是unreahable的,由第4點(diǎn)可知歧胁,將對(duì)象finalizable對(duì)象標(biāo)記為finalized時(shí)會(huì)由某個(gè)線程執(zhí)行該對(duì)象的finalize方法,致使其變成reachable厉碟。這也是圖中只有八個(gè)狀態(tài)點(diǎn)的原因
(6)程序員手動(dòng)調(diào)用finalize方法并不會(huì)影響到上述內(nèi)部標(biāo)記的變化喊巍,因此JVM只會(huì)至多調(diào)用finalize一次,即使該對(duì)象“復(fù)活”也是如此箍鼓。程序員手動(dòng)調(diào)用多少次不影響JVM的行為
(7)若JVM檢測(cè)到finalized狀態(tài)的對(duì)象變成unreachable崭参,回收其內(nèi)存(I)
(8)若對(duì)象并未覆蓋finalize方法,JVM會(huì)進(jìn)行優(yōu)化款咖,直接回收對(duì)象(O)
(9)注:System.runFinalizersOnExit()等方法可以使對(duì)象即使處于reachable狀態(tài)何暮,JVM仍對(duì)其執(zhí)行finalize方法

總結(jié)

根據(jù)GC的工作原理,我們可以通過一些技巧和方式铐殃,讓GC運(yùn)行更加有效率海洼,更加符合應(yīng)用程序的要求。一些關(guān)于程序設(shè)計(jì)的幾點(diǎn)建議:

1.最基本的建議就是盡早釋放無用對(duì)象的引用富腊。大多數(shù)程序員在使用臨時(shí)變量的時(shí)候坏逢,都是讓引用變量在退出活動(dòng)域(scope)后,自動(dòng)設(shè)置為 null.我們?cè)谑褂眠@種方式時(shí)候赘被,必須特別注意一些復(fù)雜的對(duì)象圖是整,例如數(shù)組,隊(duì)列民假,樹浮入,圖等,這些對(duì)象之間有相互引用關(guān)系較為復(fù)雜羊异。對(duì)于這類對(duì)象事秀,GC 回收它們一般效率較低。如果程序允許球化,盡早將不用的引用對(duì)象賦為null.這樣可以加速GC的工作秽晚。

2.盡量少用finalize函數(shù)。finalize函數(shù)是Java提供給程序員一個(gè)釋放對(duì)象或資源的機(jī)會(huì)筒愚。但是赴蝇,它會(huì)加大GC的工作量,因此盡量少采用finalize方式回收資源巢掺。

3.如果需要使用經(jīng)常使用的圖片句伶,可以使用soft應(yīng)用類型劲蜻。它可以盡可能將圖片保存在內(nèi)存中,供程序調(diào)用考余,而不引起OutOfMemory.

4.注意集合數(shù)據(jù)類型先嬉,包括數(shù)組,樹楚堤,圖疫蔓,鏈表等數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)對(duì)GC來說身冬,回收更為復(fù)雜衅胀。另外,注意一些全局的變量酥筝,以及一些靜態(tài)變量滚躯。這些變量往往容易引起懸掛對(duì)象(dangling reference),造成內(nèi)存浪費(fèi)嘿歌。

5.當(dāng)程序有一定的等待時(shí)間掸掏,程序員可以手動(dòng)執(zhí)行System.gc(),通知GC運(yùn)行宙帝,但是Java語言規(guī)范并不保證GC一定會(huì)執(zhí)行丧凤。使用增量式GC可以縮短Java程序的暫停時(shí)間。

參考

https://blog.csdn.net/laomo_bible/article/details/83112622
https://blog.csdn.net/weixin_33923762/article/details/88030825
https://blog.csdn.net/jisuanjiguoba/article/details/80156781

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茄唐,一起剝皮案震驚了整個(gè)濱河市息裸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沪编,老刑警劉巖呼盆,帶你破解...
    沈念sama閱讀 212,686評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蚁廓,居然都是意外死亡访圃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門相嵌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腿时,“玉大人,你說我怎么就攤上這事饭宾∨悖” “怎么了?”我有些...
    開封第一講書人閱讀 158,160評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵看铆,是天一觀的道長(zhǎng)徽鼎。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么否淤? 我笑而不...
    開封第一講書人閱讀 56,736評(píng)論 1 284
  • 正文 為了忘掉前任悄但,我火速辦了婚禮,結(jié)果婚禮上石抡,老公的妹妹穿的比我還像新娘檐嚣。我一直安慰自己,他們只是感情好啰扛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評(píng)論 6 386
  • 文/花漫 我一把揭開白布嚎京。 她就那樣靜靜地躺著,像睡著了一般隐解。 火紅的嫁衣襯著肌膚如雪挖藏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,043評(píng)論 1 291
  • 那天厢漩,我揣著相機(jī)與錄音,去河邊找鬼岩臣。 笑死溜嗜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的架谎。 我是一名探鬼主播炸宵,決...
    沈念sama閱讀 39,129評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼谷扣!你這毒婦竟也來了土全?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,872評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤会涎,失蹤者是張志新(化名)和其女友劉穎裹匙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體末秃,經(jīng)...
    沈念sama閱讀 44,318評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡概页,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了练慕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惰匙。...
    茶點(diǎn)故事閱讀 38,777評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖铃将,靈堂內(nèi)的尸體忽然破棺而出项鬼,到底是詐尸還是另有隱情,我是刑警寧澤劲阎,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布绘盟,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏奥此。R本人自食惡果不足惜弧哎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稚虎。 院中可真熱鬧撤嫩,春花似錦、人聲如沸蠢终。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寻拂。三九已至程奠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間祭钉,已是汗流浹背瞄沙。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留慌核,地道東北人距境。 一個(gè)月前我還...
    沈念sama閱讀 46,589評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像垮卓,于是被迫代替她去往敵國(guó)和親垫桂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • github上的地址:DevelopBlog 與C語言不同粟按,Java內(nèi)存(堆內(nèi)存)的回收由JVM垃圾收集器自動(dòng)完成...
    Vinctor閱讀 692評(píng)論 0 2
  • 導(dǎo)讀: 移動(dòng)開發(fā)知識(shí)體系總章(Java基礎(chǔ)诬滩、Android、Flutter) Java中GC是什么 Java中GC...
    彭空空閱讀 744評(píng)論 0 3
  • 1. 為什么需要垃圾回收 如果不進(jìn)行垃圾回收灭将,內(nèi)存遲早被消耗空疼鸟,因?yàn)槲覀冊(cè)诓粩喾峙鋬?nèi)存而沒有回收,除非內(nèi)存無限大...
    世道無情閱讀 285評(píng)論 1 0
  • Java 中有垃圾回收器回收無用對(duì)象占用的內(nèi)存庙曙,這也真是Java可以屏蔽掉C語言中類似指針和內(nèi)存分配malloc的...
    布衣不才Jerry閱讀 430評(píng)論 0 0
  • GC是什么愚臀?為什么要有GC?原文地址 GC是垃圾收集的意思矾利,內(nèi)存處理是編程人員容易出現(xiàn)問題的地方姑裂,忘記或者錯(cuò)...
    雨筍情緣閱讀 258評(píng)論 0 0