Java虛擬機(jī)筆記 jvm notes(II GC)

垃圾回收機(jī)制

比較好的文章:


1??可回收對(duì)象算法

  • 目前查看對(duì)象是否需要回收的算法主要由兩種:引用計(jì)數(shù)法可達(dá)性分析;
  • 引用計(jì)數(shù)雖好,但是無(wú)法解決對(duì)象之間相互循環(huán)引用但是實(shí)際上這些對(duì)象沒(méi)什么卵用時(shí)造成的內(nèi)存泄漏;

  • java采用的是可達(dá)性分析算法(Reachability Analysis)來(lái)判斷對(duì)象是否存活:從GC Roots出發(fā),可以到達(dá)的對(duì)象就是存活的,到達(dá)不了的就要被GC;

  • GC Roots可以是虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象、方法區(qū)中類靜態(tài)屬性引用的對(duì)象、方法區(qū)中產(chǎn)量引用的對(duì)象、本地方法棧引用的對(duì)象;這些對(duì)象的特點(diǎn)就是不被GC管理(GC回收的是堆上對(duì)象,GC roots位于方法區(qū)、虛擬機(jī)棧和本地方法棧),可以作為GC Roots的對(duì)象見(jiàn)可以作為GC Roots的對(duì)象;

  • 一個(gè)對(duì)象真正的死亡需要兩次被標(biāo)記的過(guò)程:如果對(duì)象在進(jìn)行可達(dá)性分析后沒(méi)有發(fā)現(xiàn)和GC Roots相連的引用鏈,將會(huì)被第一次標(biāo)記并進(jìn)行一次篩選,篩選條件是此對(duì)象是否需要執(zhí)行finalize()方法,當(dāng)對(duì)象沒(méi)有重寫覆蓋finalize()方法或者finalize()方法已經(jīng)執(zhí)行過(guò)一次,則虛擬機(jī)認(rèn)為不需要執(zhí)行finalize()
    如果虛擬機(jī)認(rèn)為需要執(zhí)行finalize(),則會(huì)將對(duì)象放入F-Queue隊(duì)列中,等待Finalizer線程執(zhí)行觸發(fā)finalize()方法。在這個(gè)隊(duì)列等待過(guò)程中,如果重新和引用鏈上的對(duì)象發(fā)生關(guān)聯(lián)(重新引用),第二次標(biāo)記的時(shí)候?qū)?huì)移除“等待回收”的對(duì)象集合,這個(gè)對(duì)象就重新存活了。若沒(méi)有,將會(huì)被第二次標(biāo)記然后被回收。

  • 引用分為強(qiáng)引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference),這四種強(qiáng)度逐漸減弱。


四種引用介紹:

  1. 強(qiáng)應(yīng)用:就是我們代碼中最常見(jiàn)的引用方式,GC在強(qiáng)引用對(duì)象可達(dá)時(shí)不會(huì)回收;
  2. 軟引用:使用java.lang.ref.SoftReference來(lái)創(chuàng)建軟引用,軟引用的特點(diǎn)是當(dāng)內(nèi)存不足時(shí),GC會(huì)將其回收;適合作為緩存使用
  3. 弱引用:使用java.lang.ref.WeakReference來(lái)創(chuàng)建弱引用,特點(diǎn)是每次GC都會(huì)將其回收,無(wú)論內(nèi)存充足與否;
  4. 虛引用:使用java.lang.ref.PhantomReference來(lái)創(chuàng)建虛引用,其特點(diǎn)是和引用它的對(duì)象生命周期無(wú)關(guān),GC任何情況下都會(huì)回收它。虛引用僅用來(lái)處理資源的清理問(wèn)題,比Object里面的finalize機(jī)制更靈活。get方法返回的永遠(yuǎn)是null,Java虛擬機(jī)不負(fù)責(zé)清理虛引用,但是它會(huì)把虛引用放到引用隊(duì)列里面。
    使用:對(duì)于軟引用和弱引用,可以有兩種構(gòu)造方式:使用ReferenceQueue引用隊(duì)列或者不使用ReferenceQueue,例如:
WeakReference<String> wr = new WeakReference<String>(str);

或者

ReferenceQueue<String> rq = new ReferenceQueue<String>();
WeakReference<String> wr = new WeakReference<String>(str, rq);

軟引用和弱引用很相似,也是最常用的非強(qiáng)引用,
而對(duì)于虛引用PhantomReference只能配合ReferenceQueue使用:

ReferenceQueue<String> rq = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(str, rq);


2??垃圾收集算法

  • 當(dāng)對(duì)象判斷為可回收后,需要專門的算法去執(zhí)行回收動(dòng)作。垃圾收集算法主要分為以下幾種:
  • 標(biāo)記-清除算法,Mark-Sweep:顧名思義,缺點(diǎn)是效率慢,容易造成內(nèi)存碎片;
  • 復(fù)制算法,Coping:將內(nèi)存空間分為幾塊區(qū)域,每次只使用其中一塊E,當(dāng)一塊快用完了,就將存活的對(duì)象復(fù)制到另外一塊S上,然后把使用的E一次性清除干凈。實(shí)際上這種算法用于新生代的垃圾回收,E即Eden,S就是Survivor,缺點(diǎn)是Survivor空間不夠時(shí)需要老年代內(nèi)存來(lái)?yè)?dān)保,即所謂的分配擔(dān)保(Handle Promoting);
  • 標(biāo)記整理算法,Mark-Compact:和標(biāo)記-清除算法的區(qū)別在于,標(biāo)記后并不直接進(jìn)行清理,而是將存活對(duì)象向內(nèi)存空間的一端移動(dòng),然后直接清除存活邊界以外的內(nèi)存空間,避免碎片化;
  • 分代收集算法,Generational Collection:實(shí)際上就是上述垃圾收集算法的組合,即在不同的內(nèi)存區(qū)域采用不同的收集算法。比如在新生代采用復(fù)制算法,在老年代采用標(biāo)記-整理/清理算法。
  • 在Hotspot中使用一組稱為OopMap的數(shù)據(jù)結(jié)構(gòu)來(lái)達(dá)到這個(gè)目的,在類加載完畢的時(shí)候,HotSpot就會(huì)把對(duì)象內(nèi)什么偏移量上是什么類型的數(shù)據(jù)計(jì)算出來(lái)。在JIT中也會(huì)在特定的位置記錄下棧和寄存器的引用的位置。
  • HotSpot并非為每條指令都生成OopMap,只在安全點(diǎn)SafePoint記錄。即程序執(zhí)行到安全點(diǎn)才暫停下來(lái)開(kāi)始GC。
  • 收集器的性能指標(biāo):
    1??吞吐率(1 - GC總時(shí)間/總時(shí)間):高吞吐量能提高CPU效率,適合交互較少的程序;
    2??最大暫停時(shí)間:越短越適合交互比較多的程序;
    3??堆空間使用率:
  • 常見(jiàn)的收集器:
  1. Serial:?jiǎn)尉€程收集器,新生代運(yùn)行,使用復(fù)制算法,在安全點(diǎn)暫停所有線程開(kāi)始GC;
  2. ParNew:多線程版本的Serial,新生代運(yùn)行;優(yōu)先配合CMS
  3. Parallel Scavenge:新生代收集器,使用復(fù)制算法,可以控制吞吐量,吞吐量?jī)?yōu)先收集器;配合Serial Old使用,不能搭配CMS;
  4. Serial Old:Serial的老年代版本,老年代運(yùn)行,使用標(biāo)記-整理算法;
  5. Parallel Old:Parallel Scavenge的老年代版本,優(yōu)先考慮配合Parallel Scavenge;
  6. CMS:Concurrent Mark Sweep,獲取最短暫停時(shí)間,老年代運(yùn)行;
  7. G1:Garbage First,最新的收集器,老年代和新生代都可以使用。
GC 描述 適用年代 VM參數(shù) 偏重 搭配
Serial 單線程收集器 新生代 -XX:+UseSerialGC 單核CPU VM參數(shù)下同時(shí)啟動(dòng)Serial Old
ParNew 多線程收集器 新生代 -XX:+UseConcMarkSweepGC或-XX:+UseParNewGC 多核CPU CMS
Parallel Scavenge 并行多線程 新生代 -XX:+UseParallelGC 吞吐量 VM參數(shù)下默認(rèn)Serial Old
Serial Old Serial的老年代版本 老年代 -XX:+UseSerialGC CMS的替補(bǔ) Parallel Scavenge(jdk1.5以前)
Parallel Old Parallel Scavenge的老年代版本 老年代 -XX:-UseParallelOldGC 吞吐量/CPU資源 Parallel Scavenge
CMS Concurrent Mark Sweep 老年代 -XX:+UseConcMarkSweepGC 暫停時(shí)間 -XX:+UseConcMarkSweepGC同時(shí)啟動(dòng)ParNew
G1 優(yōu)先搜集占用空間大的垃圾 全局 -XX:+UseG1GC 在暫停時(shí)間內(nèi)盡可能收集多內(nèi)存 全局使用
  • jdk 7默認(rèn)的GC回收器組合是ParNew + CMS;

  • 一個(gè)典型的32位JVM,Java堆大小設(shè)置在2 GB(使用分代&并發(fā)收集器)通常為500 MB YoungGen分配空間和1.5 GB的OldGen空間。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容