最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊

垃圾收集策略與算法

2023-06-30 12:28 作者:全家桶激活  | 我要投稿

程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧隨線程而生,也隨線程而滅;棧幀隨著方法的開始而入棧,隨著方法的結(jié)束而出棧。這幾個(gè)區(qū)域的內(nèi)存分配和回收都具有確定性,在這幾個(gè)區(qū)域內(nèi)不需要過多考慮回收的問題,因?yàn)榉椒ńY(jié)束或者線程結(jié)束時(shí),內(nèi)存自然就跟隨著回收了。

而對于 Java 堆和方法區(qū),我們只有在程序運(yùn)行期間才能知道會(huì)創(chuàng)建哪些對象,這部分內(nèi)存的分配和回收都是動(dòng)態(tài)的,垃圾收集器所關(guān)注的正是這部分內(nèi)存。

判定對象是否存活

若一個(gè)對象不被任何對象或變量引用,那么它就是無效對象,需要被回收。

引用計(jì)數(shù)法

在對象頭維護(hù)著一個(gè) counter 計(jì)數(shù)器,對象被引用一次則計(jì)數(shù)器 +1;若引用失效則計(jì)數(shù)器 -1。當(dāng)計(jì)數(shù)器為 0 時(shí),就認(rèn)為該對象無效了。

引用計(jì)數(shù)算法的實(shí)現(xiàn)簡單,判定效率也很高,在大部分情況下它都是一個(gè)不錯(cuò)的算法。但是主流的 Java 虛擬機(jī)里沒有選用引用計(jì)數(shù)算法來管理內(nèi)存,主要是因?yàn)樗茈y解決對象之間循環(huán)引用的問題。(雖然循環(huán)引用的問題可通過 Recycler 算法解決,但是在多線程環(huán)境下,引用計(jì)數(shù)變更也要進(jìn)行昂貴的同步操作,性能較低,早期的編程語言會(huì)采用此算法。)

舉個(gè)栗子 ?? 對象 objA 和 objB 都有字段 instance,令 objA.instance = objB 并且 objB.instance = objA,由于它們互相引用著對方,導(dǎo)致它們的引用計(jì)數(shù)都不為 0,于是引用計(jì)數(shù)算法無法通知 GC 收集器回收它們。

可達(dá)性分析法

所有和 GC Roots 直接或間接關(guān)聯(lián)的對象都是有效對象,和 GC Roots 沒有關(guān)聯(lián)的對象就是無效對象。

GC Roots 是指:

  • Java 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象

  • 本地方法棧中引用的對象

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

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

GC Roots 并不包括堆中對象所引用的對象,這樣就不會(huì)有循環(huán)引用的問題。

引用的種類

判定對象是否存活與“引用”有關(guān)。在 JDK 1.2 以前,Java 中的引用定義很傳統(tǒng),一個(gè)對象只有被引用或者沒有被引用兩種狀態(tài),我們希望能描述這一類對象:當(dāng)內(nèi)存空間還足夠時(shí),則保留在內(nèi)存中;如果內(nèi)存空間在進(jìn)行垃圾收集后還是非常緊張,則可以拋棄這些對象。很多系統(tǒng)的緩存功能都符合這樣的應(yīng)用場景。

在 JDK 1.2 之后,Java 對引用的概念進(jìn)行了擴(kuò)充,將引用分為了以下四種。不同的引用類型,主要體現(xiàn)的是對象不同的可達(dá)性狀態(tài)reachable和垃圾收集的影響。

強(qiáng)引用(Strong Reference)

類似 "Object obj = new Object()" 這類的引用,就是強(qiáng)引用,只要強(qiáng)引用存在,垃圾收集器永遠(yuǎn)不會(huì)回收被引用的對象。但是,如果我們錯(cuò)誤地保持了強(qiáng)引用,比如:賦值給了 static 變量,那么對象在很長一段時(shí)間內(nèi)不會(huì)被回收,會(huì)產(chǎn)生內(nèi)存泄漏。

軟引用(Soft Reference)

軟引用是一種相對強(qiáng)引用弱化一些的引用,可以讓對象豁免一些垃圾收集,只有當(dāng) JVM 認(rèn)為內(nèi)存不足時(shí),才會(huì)去試圖回收軟引用指向的對象。JVM 會(huì)確保在拋出 OutOfMemoryError 之前,清理軟引用指向的對象。軟引用通常用來實(shí)現(xiàn)內(nèi)存敏感的緩存,如果還有空閑內(nèi)存,就可以暫時(shí)保留緩存,當(dāng)內(nèi)存不足時(shí)清理掉,這樣就保證了使用緩存的同時(shí),不會(huì)耗盡內(nèi)存。

弱引用(Weak Reference)

弱引用的強(qiáng)度比軟引用更弱一些。當(dāng) JVM 進(jìn)行垃圾回收時(shí),無論內(nèi)存是否充足,都會(huì)回收只被弱引用關(guān)聯(lián)的對象。

虛引用(Phantom Reference)

虛引用也稱幽靈引用或者幻影引用,它是最弱的一種引用關(guān)系。一個(gè)對象是否有虛引用的存在,完全不會(huì)對其生存時(shí)間構(gòu)成影響。它僅僅是提供了一種確保對象被 finalize 以后,做某些事情的機(jī)制,比如,通常用來做所謂的 Post-Mortem 清理機(jī)制。

回收堆中無效對象

對于可達(dá)性分析中不可達(dá)的對象,也并不是沒有存活的可能。

判定 finalize() 是否有必要執(zhí)行

JVM 會(huì)判斷此對象是否有必要執(zhí)行 finalize() 方法,如果對象沒有覆蓋 finalize() 方法,或者 finalize() 方法已經(jīng)被虛擬機(jī)調(diào)用過,那么視為“沒有必要執(zhí)行”。那么對象基本上就真的被回收了。

如果對象被判定為有必要執(zhí)行 finalize() 方法,那么對象會(huì)被放入一個(gè) F-Queue 隊(duì)列中,虛擬機(jī)會(huì)以較低的優(yōu)先級執(zhí)行這些 finalize()方法,但不會(huì)確保所有的 finalize() 方法都會(huì)執(zhí)行結(jié)束。如果 finalize() 方法出現(xiàn)耗時(shí)操作,虛擬機(jī)就直接停止指向該方法,將對象清除。

對象重生或死亡

如果在執(zhí)行 finalize() 方法時(shí),將 this 賦給了某一個(gè)引用,那么該對象就重生了。如果沒有,那么就會(huì)被垃圾收集器清除。

任何一個(gè)對象的 finalize() 方法只會(huì)被系統(tǒng)自動(dòng)調(diào)用一次,如果對象面臨下一次回收,它的 finalize() 方法不會(huì)被再次執(zhí)行,想繼續(xù)在 finalize() 中自救就失效了。

回收方法區(qū)內(nèi)存

方法區(qū)中存放生命周期較長的類信息、常量、靜態(tài)變量,每次垃圾收集只有少量的垃圾被清除。方法區(qū)中主要清除兩種垃圾:

  • 廢棄常量

  • 無用的類

判定廢棄常量

只要常量池中的常量不被任何變量或?qū)ο笠?,那么這些常量就會(huì)被清除掉。比如,一個(gè)字符串 "bingo" 進(jìn)入了常量池,但是當(dāng)前系統(tǒng)沒有任何一個(gè) String 對象引用常量池中的 "bingo" 常量,也沒有其它地方引用這個(gè)字面量,必要的話,"bingo"常量會(huì)被清理出常量池。

判定無用的類

判定一個(gè)類是否是“無用的類”,條件較為苛刻。

  • 該類的所有對象都已經(jīng)被清除

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

  • 該類的 java.lang.Class 對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。

一個(gè)類被虛擬機(jī)加載進(jìn)方法區(qū),那么在堆中就會(huì)有一個(gè)代表該類的對象:java.lang.Class。這個(gè)對象在類被加載進(jìn)方法區(qū)時(shí)創(chuàng)建,在方法區(qū)該類被刪除時(shí)清除。

垃圾收集算法

學(xué)會(huì)了如何判定無效對象、無用類、廢棄常量之后,剩余工作就是回收這些垃圾。常見的垃圾收集算法有以下幾個(gè):

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

標(biāo)記的過程是:遍歷所有的 GC Roots,然后將所有 GC Roots 可達(dá)的對象標(biāo)記為存活的對象

清除的過程將遍歷堆中所有的對象,將沒有標(biāo)記的對象全部清除掉。與此同時(shí),清除那些被標(biāo)記過的對象的標(biāo)記,以便下次的垃圾回收。

這種方法有兩個(gè)不足

  • 效率問題:標(biāo)記和清除兩個(gè)過程的效率都不高。

  • 空間問題:標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,碎片太多可能導(dǎo)致以后需要分配較大對象時(shí),無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作。

復(fù)制算法(新生代)

為了解決效率問題,“復(fù)制”收集算法出現(xiàn)了。它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊內(nèi)存用完,需要進(jìn)行垃圾收集時(shí),就將存活者的對象復(fù)制到另一塊上面,然后將第一塊內(nèi)存全部清除。這種算法有優(yōu)有劣:

  • 優(yōu)點(diǎn):不會(huì)有內(nèi)存碎片的問題。

  • 缺點(diǎn):內(nèi)存縮小為原來的一半,浪費(fèi)空間。

為了解決空間利用率問題,可以將內(nèi)存分為三塊: Eden、From Survivor、To Survivor,比例是 8:1:1,每次使用 Eden 和其中一塊 Survivor。回收時(shí),將 Eden 和 Survivor 中還存活的對象一次性復(fù)制到另外一塊 Survivor 空間上,最后清理掉 Eden 和剛才使用的 Survivor 空間。這樣只有 10% 的內(nèi)存被浪費(fèi)。

但是我們無法保證每次回收都只有不多于 10% 的對象存活,當(dāng) Survivor 空間不夠,需要依賴其他內(nèi)存(指老年代)進(jìn)行分配擔(dān)保。

分配擔(dān)保

為對象分配內(nèi)存空間時(shí),如果 Eden+Survivor 中空閑區(qū)域無法裝下該對象,會(huì)觸發(fā) MinorGC 進(jìn)行垃圾收集。但如果 Minor GC 過后依然有超過 10% 的對象存活,這樣存活的對象直接通過分配擔(dān)保機(jī)制進(jìn)入老年代,然后再將新對象存入 Eden 區(qū)。

標(biāo)記-整理算法(老年代)

標(biāo)記:它的第一個(gè)階段與標(biāo)記-清除算法是一模一樣的,均是遍歷 GC Roots,然后將存活的對象標(biāo)記。

整理:移動(dòng)所有存活的對象,且按照內(nèi)存地址次序依次排列,然后將末端內(nèi)存地址以后的內(nèi)存全部回收。因此,第二階段才稱為整理階段。

這是一種老年代的垃圾收集算法。老年代的對象一般壽命比較長,因此每次垃圾回收會(huì)有大量對象存活,如果采用復(fù)制算法,每次需要復(fù)制大量存活的對象,效率很低。

分代收集算法

根據(jù)對象存活周期的不同,將內(nèi)存劃分為幾塊。一般是把 Java 堆分為新生代和老年代,針對各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/p>

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

  • 老年代:標(biāo)記-清除算法、標(biāo)記-整理算法

本文由mdnice多平臺(tái)發(fā)布

本文使用 文章同步助手 同步

垃圾收集策略與算法的評論 (共 條)

分享到微博請遵守國家法律
柳林县| 扎兰屯市| 比如县| 崇信县| 门源| 洞口县| 阿勒泰市| 永登县| 庆阳市| 江川县| 得荣县| 望城县| 冷水江市| 金沙县| 安龙县| 伊吾县| 江油市| 鲁山县| 集贤县| 吐鲁番市| 黄浦区| 沙湾县| 长沙市| 兴和县| 马龙县| 东辽县| 玉溪市| 志丹县| 怀远县| 汉中市| 兴海县| 新泰市| 精河县| 临海市| 旺苍县| 开江县| 嘉义县| 广宗县| 南平市| 来宾市| 海兴县|