国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

深入理解JVM(三)——垃圾收集策略詳解

2019-11-10 16:58:39
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

http://blog.csdn.net/u010425776/article/details/51189318

java虛擬機(jī)的內(nèi)存模型分為五個(gè)部分,分別是:程序計(jì)數(shù)器、Java虛擬機(jī)棧、本地方法棧、堆、方法區(qū)。

這五個(gè)區(qū)域既然是存儲(chǔ)空間,那么為了避免Java虛擬機(jī)在運(yùn)行期間內(nèi)存存滿的情況,就必須得有一個(gè)垃圾收集者的角色,不定期地回收一些無(wú)效內(nèi)存,以保障Java虛擬機(jī)能夠健康地持續(xù)運(yùn)行。

這個(gè)垃圾收集者就是平常我們所說(shuō)的“垃圾收集器”,那么垃圾收集器在何時(shí)清掃內(nèi)存?清掃哪些數(shù)據(jù)?這就是接下來(lái)我們要解決的問(wèn)題。 

程序計(jì)數(shù)器、Java虛擬機(jī)棧、本地方法棧都是線程私有的,也就是每條線程都擁有這三塊區(qū)域,而且會(huì)隨著線程的創(chuàng)建而創(chuàng)建,線程的結(jié)束而銷毀。那么,垃圾收集器在何時(shí)清掃這三塊區(qū)域的問(wèn)題就解決了。

此外,Java虛擬機(jī)棧、本地方法棧中的棧幀會(huì)隨著方法的開(kāi)始而入棧,方法的結(jié)束而出棧,并且每個(gè)棧幀中的本地變量表都是在類被加載的時(shí)候就確定的。因此以上三個(gè)區(qū)域的垃圾收集工作具有確定性,垃圾收集器能夠清楚地知道何時(shí)清掃這三塊區(qū)域中的哪些數(shù)據(jù)。

然而,堆和方法區(qū)中的內(nèi)存清理工作就沒(méi)那么容易了。 堆和方法區(qū)所有線程共享,并且都在JVM啟動(dòng)時(shí)創(chuàng)建,一直得運(yùn)行到JVM停止時(shí)。因此它們沒(méi)辦法根據(jù)線程的創(chuàng)建而創(chuàng)建、線程的結(jié)束而釋放。

堆中存放JVM運(yùn)行期間的所有對(duì)象,雖然每個(gè)對(duì)象的內(nèi)存大小在加載該對(duì)象所屬類的時(shí)候就確定了,但究竟創(chuàng)建多少個(gè)對(duì)象只有在程序運(yùn)行期間才能確定。 方法區(qū)中存放類信息、靜態(tài)成員變量、常量。類的加載是在程序運(yùn)行過(guò)程中,當(dāng)需要?jiǎng)?chuàng)建這個(gè)類的對(duì)象時(shí)才會(huì)加載這個(gè)類。因此,JVM究竟要加載多少個(gè)類也需要在程序運(yùn)行期間確定。 因此,堆和方法區(qū)的內(nèi)存回收具有不確定性,因此垃圾收集器在回收堆和方法區(qū)內(nèi)存的時(shí)候花了一些心思。 

堆內(nèi)存的回收

1. 如何判定哪些對(duì)象需要回收?

在對(duì)堆進(jìn)行對(duì)象回收之前,首先要判斷哪些是無(wú)效對(duì)象。我們知道,一個(gè)對(duì)象不被任何對(duì)象或變量引用,那么就是無(wú)效對(duì)象,需要被回收。一般有兩種判別方式:

引用計(jì)數(shù)法 每個(gè)對(duì)象都有一個(gè)計(jì)數(shù)器,當(dāng)這個(gè)對(duì)象被一個(gè)變量或另一個(gè)對(duì)象引用一次,該計(jì)數(shù)器加一;若該引用失效則計(jì)數(shù)器減一。當(dāng)計(jì)數(shù)器為0時(shí),就認(rèn)為該對(duì)象是無(wú)效對(duì)象。

可達(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ū)中靜態(tài)屬性引用的對(duì)象方法區(qū)中常量所引用的對(duì)象本地方法棧所引用的對(duì)象 PS:注意!GC Roots并不包括堆中對(duì)象所引用的對(duì)象!這樣就不會(huì)出現(xiàn)循環(huán)引用。

兩者對(duì)比: 引用計(jì)數(shù)法雖然簡(jiǎn)單,但存在一個(gè)嚴(yán)重的問(wèn)題,它無(wú)法解決循環(huán)引用的問(wèn)題。 因此,目前主流語(yǔ)言均使用可達(dá)性分析方法來(lái)判斷對(duì)象是否有效。

2. 回收無(wú)效對(duì)象的過(guò)程

當(dāng)JVM篩選出失效的對(duì)象之后,并不是立即清除,而是再給對(duì)象一次重生的機(jī)會(huì),具體過(guò)程如下:

判斷該對(duì)象是否覆蓋了finalize()方法

若已覆蓋該方法,并該對(duì)象的finalize()方法還沒(méi)有被執(zhí)行過(guò),那么就會(huì)將finalize()扔到F-Queue隊(duì)列中;若未覆蓋該方法,則直接釋放對(duì)象內(nèi)存。

執(zhí)行F-Queue隊(duì)列中的finalize()方法 虛擬機(jī)會(huì)以較低的優(yōu)先級(jí)執(zhí)行這些finalize()方法們,也不會(huì)確保所有的finalize()方法都會(huì)執(zhí)行結(jié)束。如果finalize()方法中出現(xiàn)耗時(shí)操作,虛擬機(jī)就直接停止執(zhí)行,將該對(duì)象清除。

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

注意: 強(qiáng)烈不建議使用finalize()函數(shù)進(jìn)行任何操作!如果需要釋放資源,請(qǐng)使用try-finally。 因?yàn)閒inalize()不確定性大,開(kāi)銷大,無(wú)法保證順利執(zhí)行。

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

我們知道,如果使用復(fù)制算法實(shí)現(xiàn)堆的內(nèi)存回收,堆就會(huì)被分為新生代和老年代,新生代中的對(duì)象“朝生夕死”,每次垃圾回收都會(huì)清除掉大量的對(duì)象;而老年代中的對(duì)象生命較長(zhǎng),每次垃圾回收只有少量的對(duì)象被清除掉。

由于方法區(qū)中存放生命周期較長(zhǎng)的類信息、常量、靜態(tài)變量,因此方法區(qū)就像是堆的老年代,每次垃圾收集的只有少量的垃圾被清除掉。

方法區(qū)中主要清除兩種垃圾: 1. 廢棄常量 2. 廢棄的類

1. 如何判定廢棄常量?

清除廢棄的常量和清除對(duì)象類似,只要常量池中的常量不被任何變量或?qū)ο笠茫敲催@些常量就會(huì)被清除掉。

2. 如何廢棄廢棄的類?

清除廢棄類的條件較為苛刻: 1. 該類的所有對(duì)象都已被清除 2. 該類的java.lang.Class對(duì)象沒(méi)有被任何對(duì)象或變量引用 只要一個(gè)類被虛擬機(jī)加載進(jìn)方法區(qū),那么在堆中就會(huì)有一個(gè)代表該類的對(duì)象:java.lang.Class。這個(gè)對(duì)象在類被加載進(jìn)方法區(qū)的時(shí)候創(chuàng)建,在方法區(qū)中該類被刪除時(shí)清除。 3. 加載該類的ClassLoader已經(jīng)被回收

垃圾收集算法

現(xiàn)在我們知道了判定一個(gè)對(duì)象是無(wú)效對(duì)象、判定一個(gè)類是廢棄類、判定一個(gè)常量是廢棄常量的方法,也就是知道了垃圾收集器會(huì)清除哪些數(shù)據(jù),那么接下來(lái)介紹如何清除這些數(shù)據(jù)。

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

首先利用剛才介紹的方法判斷需要清除哪些數(shù)據(jù),并給它們做上標(biāo)記;然后清除被標(biāo)記的數(shù)據(jù)。

分析: 這種算法標(biāo)記和清除過(guò)程效率都很低,而且清除完后存在大量碎片空間,導(dǎo)致無(wú)法存儲(chǔ)大對(duì)象,降低了空間利用率。

2. 復(fù)制算法

將內(nèi)存分成兩份,只將數(shù)據(jù)存儲(chǔ)在其中一塊上。當(dāng)需要回收垃圾時(shí),也是首先標(biāo)記出廢棄的數(shù)據(jù),然后將有用的數(shù)據(jù)復(fù)制到另一塊內(nèi)存上,最后將第一塊內(nèi)存全部清除。

分析: 這種算法避免了碎片空間,但內(nèi)存被縮小了一半。 而且每次都需要將有用的數(shù)據(jù)全部復(fù)制到另一片內(nèi)存上去,效率不高。

解決空間利用率問(wèn)題: 在新生代中,由于大量的對(duì)象都是“朝生夕死”,也就是一次垃圾收集后只有少量對(duì)象存活,因此我們可以將內(nèi)存劃分成三塊:Eden、Survior1、Survior2,內(nèi)存大小分別是8:1:1。分配內(nèi)存時(shí),只使用Eden和一塊Survior1。當(dāng)發(fā)現(xiàn)Eden+Survior1的內(nèi)存即將滿時(shí),JVM會(huì)發(fā)起一次MinorGC,清除掉廢棄的對(duì)象,并將所有存活下來(lái)的對(duì)象復(fù)制到另一塊Survior2中。那么,接下來(lái)就使用Survior2+Eden進(jìn)行內(nèi)存分配。

通過(guò)這種方式,只需要浪費(fèi)10%的內(nèi)存空間即可實(shí)現(xiàn)帶有壓縮功能的垃圾收集方法,避免了內(nèi)存碎片的問(wèn)題。

但是,當(dāng)一個(gè)對(duì)象要申請(qǐng)內(nèi)存空間時(shí),發(fā)現(xiàn)Eden+Survior中剩下的空間無(wú)法放置該對(duì)象,此時(shí)需要進(jìn)行Minor GC,如果MinorGC過(guò)后空閑出來(lái)的內(nèi)存空間仍然無(wú)法放置該對(duì)象,那么此時(shí)就需要將對(duì)象轉(zhuǎn)移到老年代中,這種方式叫做“分配擔(dān)保”。

什么是分配擔(dān)保? 當(dāng)JVM準(zhǔn)備為一個(gè)對(duì)象分配內(nèi)存空間時(shí),發(fā)現(xiàn)此時(shí)Eden+Survior中空閑的區(qū)域無(wú)法裝下該對(duì)象,那么就會(huì)觸發(fā)MinorGC,對(duì)該區(qū)域的廢棄對(duì)象進(jìn)行回收。但如果MinorGC過(guò)后只有少量對(duì)象被回收,仍然無(wú)法裝下新對(duì)象,那么此時(shí)需要將Eden+Survior中的所有對(duì)象都轉(zhuǎn)移到老年代中,然后再將新對(duì)象存入Eden區(qū)。這個(gè)過(guò)程就是“分配擔(dān)保”。

3. 標(biāo)記-整理算法

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

分析: 它是一種老年代的垃圾收集算法。老年代中的對(duì)象一般壽命比較長(zhǎng),因此每次垃圾回收會(huì)有大量對(duì)象存活,因此如果選用“復(fù)制”算法,每次需要復(fù)制大量存活的對(duì)象,會(huì)導(dǎo)致效率很低。而且,在新生代中使用“復(fù)制”算法,當(dāng)Eden+Survior中都裝不下某個(gè)對(duì)象時(shí),可以使用老年代的內(nèi)存進(jìn)行“分配擔(dān)保”,而如果在老年代使用該算法,那么在老年代中如果出現(xiàn)Eden+Survior裝不下某個(gè)對(duì)象時(shí),沒(méi)有其他區(qū)域給他作分配擔(dān)保。因此,老年代中一般使用“標(biāo)記-整理”算法。

4. 分代收集算法

將內(nèi)存劃分為老年代和新生代。老年代中存放壽命較長(zhǎng)的對(duì)象,新生代中存放“朝生夕死”的對(duì)象。然后在不同的區(qū)域使用不同的垃圾收集算法。

Java中引用的種類

Java中根據(jù)生命周期的長(zhǎng)短,將引用分為4類。

1. 強(qiáng)引用

我們平時(shí)所使用的引用就是強(qiáng)引用。 A a = new A(); 也就是通過(guò)關(guān)鍵字new創(chuàng)建的對(duì)象所關(guān)聯(lián)的引用就是強(qiáng)引用。 只要強(qiáng)引用存在,該對(duì)象永遠(yuǎn)也不會(huì)被回收。 

2. 軟引用

只有當(dāng)堆即將發(fā)生OOM異常時(shí),JVM才會(huì)回收軟引用所指向的對(duì)象。 軟引用通過(guò)SoftReference類實(shí)現(xiàn)。 軟引用的生命周期比強(qiáng)引用短一些。 

3. 弱引用

只要垃圾收集器運(yùn)行,軟引用所指向的對(duì)象就會(huì)被回收。 弱引用通過(guò)WeakReference類實(shí)現(xiàn)。 弱引用的生命周期比軟引用短。 

4. 虛引用

虛引用也叫幽靈引用,它和沒(méi)有引用沒(méi)有區(qū)別,無(wú)法通過(guò)虛引用訪問(wèn)對(duì)象的任何屬性或函數(shù)。 一個(gè)對(duì)象關(guān)聯(lián)虛引用唯一的作用就是在該對(duì)象被垃圾收集器回收之前會(huì)受到一條系統(tǒng)通知。 虛引用通過(guò)PhantomReference類來(lái)實(shí)現(xiàn)。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 霞浦县| 阜平县| 德州市| 石河子市| 安龙县| 康乐县| 西华县| 绥中县| 鱼台县| 望奎县| 房产| 英山县| 吕梁市| 平阳县| 博罗县| 突泉县| 禹州市| 黎城县| 漠河县| 保康县| 玉林市| 潢川县| 涞源县| 岗巴县| 万州区| 蓝山县| 祁门县| 赤城县| 修武县| 贵州省| 安吉县| 饶阳县| 祁阳县| 申扎县| 新干县| 永顺县| 芜湖市| 泾阳县| 澄迈县| 宁化县| 永德县|