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