日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

java垃圾回收策论_深入理解 Java 虚拟机【3】垃圾收集策略与算法

發(fā)布時(shí)間:2024/1/23 java 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java垃圾回收策论_深入理解 Java 虚拟机【3】垃圾收集策略与算法 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

作者:楊立濱

鏈接:https://github.com/yanglbme/jvm

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

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

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

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

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

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

引用計(jì)數(shù)算法的實(shí)現(xiàn)簡(jiǎn)單,判定效率也很高,在大部分情況下它都是一個(gè)不錯(cuò)的算法。但是主流的 Java 虛擬機(jī)里沒(méi)有選用引用計(jì)數(shù)算法來(lái)管理內(nèi)存,主要是因?yàn)樗茈y解決對(duì)象之間循環(huán)引用的問(wèn)題。

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

可達(dá)性分析法

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

GC Roots 是指:

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

本地方法棧中引用的對(duì)象

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

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

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

引用的種類(lèi)

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

在 JDK 1.2 之后,Java 對(duì)引用的概念進(jìn)行了擴(kuò)充,將引用分為了以下四種:

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

類(lèi)似 "Object obj = new Object()" 這類(lèi)的引用,就是強(qiáng)引用,只要強(qiáng)引用存在,垃圾收集器永遠(yuǎn)不會(huì)回收被引用的對(duì)象。

軟引用(Soft Reference)

軟引用是用來(lái)描述一些有用但并非必需的對(duì)象,j就是說(shuō),內(nèi)存足夠時(shí)留著它們,內(nèi)存即將發(fā)生溢出時(shí)把這些對(duì)象列入回收范圍進(jìn)行回收。若回收過(guò)后還沒(méi)有足夠的內(nèi)存,才拋出內(nèi)存溢出異常。

弱引用(Weak Reference)

弱引用也是用來(lái)描述非必需對(duì)象的,但是它的強(qiáng)度比軟引用更弱一些。當(dāng) JVM 進(jìn)行垃圾回收時(shí),無(wú)論內(nèi)存是否充足,都會(huì)回收被軟引用關(guān)聯(lián)的對(duì)象。

虛引用(Phantom Reference)

虛引用也稱(chēng)幽靈引用或者幻影引用,它是最弱的一種引用關(guān)系。一個(gè)對(duì)象是否有虛引用的存在,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響。為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知。

回收堆中無(wú)效對(duì)象

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

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

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

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

對(duì)象重生或死亡

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

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

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

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

廢棄常量

無(wú)用的類(lèi)

判定廢棄常量

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

判定無(wú)用的類(lèi)

判定一個(gè)類(lèi)是否是“無(wú)用的類(lèi)”,條件較為苛刻:

該類(lèi)的所有對(duì)象都已經(jīng)被清除

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

該類(lèi)的 java.lang.Class 對(duì)象沒(méi)有在任何地方被引用,無(wú)法在任何地方通過(guò)反射訪問(wèn)該類(lèi)的方法。

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

垃圾收集算法

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

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

判斷哪些數(shù)據(jù)需要清除,并對(duì)它們進(jìn)行標(biāo)記,然后清除被標(biāo)記的數(shù)據(jù)。

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

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

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

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

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

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

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

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

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

分配擔(dān)保

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

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

在回收垃圾前,首先將廢棄對(duì)象做上標(biāo)記,然后將未標(biāo)記的對(duì)象移到一邊,最后清空另一邊區(qū)域即可。

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

分代收集算法

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

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

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

(完)

推薦大而全的【后端技術(shù)精選】

始發(fā)于微信公眾號(hào): Java知音

總結(jié)

以上是生活随笔為你收集整理的java垃圾回收策论_深入理解 Java 虚拟机【3】垃圾收集策略与算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。