JVM垃圾收集策略
JVM垃圾收集策略
1.垃圾收集
1.1 垃圾回收策略
- 對內(nèi)存要求苛刻的場景:想辦法提高對象的回收效率,多回收掉一些對象,騰出更多內(nèi)存
- 在CPU使用率高的情況下:降低高并發(fā)時(shí)垃圾回收的頻率,讓CPU更多地去執(zhí)行業(yè)務(wù)而不是垃圾回收
1.2 垃圾回收區(qū)域
堆:回收對象
方法區(qū):回收常量和類(回收頻率低)
1.3 對象回收的時(shí)機(jī)
1.3.1 引用計(jì)數(shù)算法
在對象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加一;當(dāng)引用失效時(shí),計(jì)數(shù)器值就減一;任何時(shí)候計(jì)數(shù)器值為零的對象就是不可能再被使用的。
引用計(jì)數(shù)法無效情況(循環(huán)引用):
1.3.2 可達(dá)性分析算法
基本思路:通過一系列稱為"GC Roots"的根對象作為初始節(jié)點(diǎn)集,從這些節(jié)點(diǎn)開始,根據(jù)引用關(guān)系向下搜索,搜索過程所走過的路徑稱為“引用鏈,如果某個(gè)對象到GC Roots間沒有任何引用鏈相連,則證明此對象是不可能再被使用的。
GC Roots的對象
可達(dá)性算法注意點(diǎn)
- 一個(gè)對象即使不可達(dá),也不一定會被回收
-
finalize()優(yōu)先級低,何時(shí)會被調(diào)用無法確定,因?yàn)槭裁磿r(shí)間發(fā)生GC不確定
-
盡量不使用finalize()來拯救對象,使用try-catch-finally來進(jìn)行替換
1.4 引用
JDK1.2之前:如果reference類型的數(shù)據(jù)中存儲的數(shù)值代表的是另外一塊內(nèi)存的起始地址,就稱該reference數(shù)據(jù)是代表某塊內(nèi)存、某個(gè)對象的引用。
JDK1.2后:將引用分為強(qiáng)引用、軟引用、弱引用、虛引用
1.4.1 強(qiáng)引用
強(qiáng)引用是最傳統(tǒng)的“引用”的定義,是指在程序代碼之中普遍存在的引用賦值,無論任何情況下,只要強(qiáng)引用關(guān)系還存在,垃圾收集器就永遠(yuǎn)不會回收掉被引用的對象。
- Object object = new Object()
- 只要強(qiáng)引用關(guān)系還存在,垃圾收集器就永遠(yuǎn)不會回收掉被引用的對象
1.4.2 軟引用
用來描述一些還有用,但非必須的對象。只被軟引用關(guān)聯(lián)著的對象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常前,會把這些對象列入進(jìn)回收范圍之中進(jìn)行第二次回收。
- SoftReference<String> sr = new SoftReference<>("hello");
- 用來描述一些有用但非必須的對象
- 只有在內(nèi)存不足的時(shí)候才會回收
- 可用于實(shí)現(xiàn)緩存
1.4.3 弱引用
用來描述那些非必須對象,但是它的強(qiáng)度比軟引用還要更弱一些,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生為止。當(dāng)垃圾收集器開始工作,無論內(nèi)存是否充足,都會回收掉只被弱引用關(guān)聯(lián)的對象。
- WeakReference<String> sr = new WeakReference<>("hello")
- 弱引用也是用來描述非必須的對象
- 無論內(nèi)存是否充足,都會被垃圾收集器回收
1.4.4 虛引用
虛引用也成為“幽靈引用”或者“幻影引用”,它是最弱的一種引用關(guān)系。一個(gè)對象是否有虛引用的存在,完全不會對其生存時(shí)間構(gòu)成影響,也無法通過虛引用來取消一個(gè)對象實(shí)例。唯一目的只是能在這個(gè)對象被收集器回收的時(shí)候收到一個(gè)通知
-
ReferenceQueue<String> queue = new ReferenceQueue<>();
PhantomReference<String> pr = new PhantomReference<>('hello', queue);
-
不影響對象的生命周期,如果一個(gè)對象只有虛引用,那么它就和沒有任何引用一樣,在任何時(shí)候都可以被回收。虛引用主要用來跟蹤對象被垃圾回收器回收的活動,必須要和引用隊(duì)列配合使用。當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會在回收對象的內(nèi)存之前,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。程序可以通過判斷引用隊(duì)列中是否已經(jīng)加入虛引用,來了解被引用的對象是否將要被垃圾回收。如果程序發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列,那么就可以在所引用的對象的內(nèi)存被回收之前采取必要的行動
1.5 垃圾收集算法
1.5.1 標(biāo)記-清除算法(Mark-Sweep)
首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后,統(tǒng)一回收掉所有被標(biāo)記的對象,也可以反過來,標(biāo)記存活的對象,統(tǒng)一回收未被標(biāo)記的對象。
缺點(diǎn):
- 執(zhí)行效率不穩(wěn)定 - 如果Java堆中包含大量對象,而且其中大部分是需要被回收的,這時(shí)必須進(jìn)行大量標(biāo)記和清除的操作,導(dǎo)致標(biāo)記和清除兩個(gè)過程的執(zhí)行效率都隨著對象數(shù)量增長而降低
- 內(nèi)存空間碎片化 - 標(biāo)記、清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導(dǎo)致當(dāng)以后在程序運(yùn)行過程中需要分配較大對象時(shí)無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)一次垃圾收集動作
1.5.2 標(biāo)記-復(fù)制算法(Mark-Copy)
它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中一塊。當(dāng)這一塊內(nèi)存用完了,就將還存活的對象復(fù)制到另外一塊上買了,然后再把已使用過的內(nèi)存空間一次處理掉。
缺點(diǎn):
- 將可用內(nèi)存縮小為了原來的一半
- 標(biāo)記-復(fù)制算法在對象存活率較高時(shí)就要進(jìn)行較多的復(fù)制操作,效率將會降低。更關(guān)鍵的是,如果不想浪費(fèi)50%的空間,就需要有額外的空間進(jìn)行分配擔(dān)保,以應(yīng)對被使用內(nèi)存中所有對象都100%存活的極端情況,所以在老年代一般不能直接使用這種算法
1.5.3 標(biāo)記-整理(Mark-Compact)
標(biāo)記過程仍然和標(biāo)記-清除算法一致,但后續(xù)步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向內(nèi)存空間一端移動,然后直接清理掉邊界以外的內(nèi)存。
缺點(diǎn):
- 移動存活對象需要開銷,尤其是在老年代這種每次回收都有大量對象存活區(qū)域,而且移動老年代區(qū)域?qū)ο笮枰虝和S脩魬?yīng)用程序才能進(jìn)行。
| 標(biāo)記-清除 | 實(shí)現(xiàn)簡單 | 1.空間碎片化 2.標(biāo)記和清除時(shí)間隨對象數(shù)量增長而增加 |
| 標(biāo)記-復(fù)制 | 性能好、無碎片 | 1.空間利用率低 |
| 標(biāo)記-整理 | 無碎片 | 1.開銷大 |
1.5.4 分代收集算法
基于兩個(gè)分代假說之上:
收集器將Java堆劃分出兩個(gè)不同的區(qū)域,然后將回收對象依據(jù)其年齡(年齡即對象熬過垃圾回收過程的次數(shù))分配到不同的區(qū)域之中存儲。
回收類型
- 新生代回收(Minor GC | Young GC)
- 老年代回收(Major GC)
- 清理整個(gè)堆(Full GC)
- Major GC ≈ Full GC
根據(jù)不同的區(qū)域安排與里面存儲對象存亡特征相匹配的垃圾回收算法
新生代一般采用標(biāo)記-復(fù)制算法,Eden:Survivor=8:1,當(dāng)Survivor空間不足以容納一次Minor GC之后存活的對象時(shí),就需要其他內(nèi)存區(qū)域(大多為老年代)進(jìn)行分配擔(dān)保,即如果另外一塊Survivor空間沒有足夠空間存放上一次新生代收集下來的存活對象,這些對象便將通過分配擔(dān)保機(jī)制直接進(jìn)入老年代。
老年代一般采用標(biāo)記-清除或標(biāo)記-整理算法。
重點(diǎn)
- 對象大于-XX:PretenureSizeThreshold,就會直接分配到老年代
- 新生代空間不足,分配擔(dān)保直接進(jìn)入老年代
- 動態(tài)年齡:如果Survivor空間中所有相同年齡對象大小的總和大于Survivor空間的一半,那么年齡大于等于該年齡的對象就可以直接進(jìn)入老年代
觸發(fā)垃圾回收的條件
新生代(Minor GC)
- Eden空間不足
老年代(Full GC)
- 老年代空間不足(空間實(shí)際不足或內(nèi)存碎片過多)
- 元空間不足
- 要晉升到老年代的對象說要占用的空間大于老年代的剩余空間
- 調(diào)用System.gc()
分代的好處
- 更有效的清除不再需要的對象
- 提升了垃圾回收的效率
分代收集算法調(diào)優(yōu)的原則
- 合理設(shè)置Survivor區(qū)的大小,避免內(nèi)存浪費(fèi)
- 讓GC盡量發(fā)生在新生代,盡量減少Full GC的發(fā)生
總結(jié)
- 上一篇: 用命令关闭Ubuntu的自动关闭屏幕和锁
- 下一篇: css开启3d加速的属性,开启3D加速,