垃圾回收機(jī)制主要完成下面兩件事情:
跟蹤并監(jiān)控每個(gè)Java對(duì)象,當(dāng)某個(gè)對(duì)象處于不可達(dá)狀態(tài)時(shí),回收該對(duì)象所占用的內(nèi)存;
清理內(nèi)存分配,回收過(guò)程中產(chǎn)生的內(nèi)存碎片。
實(shí)際上,垃圾回收機(jī)制不可能實(shí)時(shí)檢測(cè)到每個(gè)Java對(duì)象的狀態(tài),因此當(dāng)一個(gè)對(duì)象失去引用后,它也不會(huì)被立即回收,只有等垃圾回收運(yùn)行時(shí)才會(huì)被回收。南京Java培訓(xùn)
對(duì)于一個(gè)垃圾回收器的設(shè)計(jì)算法來(lái)說(shuō),大致有如下可供選擇的設(shè)計(jì)。
串行回收(Serial)和并行回收(Parallel):穿行回收就是不管系統(tǒng)有多少個(gè)CPU,始終只用一個(gè)CPU來(lái)執(zhí)行垃圾回收操作;而并行回收就是把整個(gè)回收工作拆分成多個(gè)部分,每個(gè)部分由一個(gè)CPU負(fù)責(zé),從而讓多個(gè)CPU并行回收。并行回收的執(zhí)行效率很高,但復(fù)雜度增加,另外也有其他一些副作用,比如內(nèi)存碎片也會(huì)增加。
并發(fā)執(zhí)行(Concurrent)和應(yīng)用程序停止(Stop-the-world):Stop-the-world的垃圾回收方式在執(zhí)行垃圾回收的同時(shí)會(huì)導(dǎo)致應(yīng)用程序的暫停。并發(fā)執(zhí)行的垃圾回收雖然不會(huì)導(dǎo)致應(yīng)用程序的暫停,但由于并發(fā)執(zhí)行垃圾回收需要解決和應(yīng)用程序的執(zhí)行沖突(應(yīng)用程序可能會(huì)在垃圾回收的過(guò)程中修改對(duì)象),因此并發(fā)執(zhí)行垃圾回收的系統(tǒng)開(kāi)銷比stop-the-world更高,并且需要更多的堆內(nèi)存。南京Java軟件培訓(xùn)
壓縮和不壓縮和復(fù)制:為了減小內(nèi)存碎片,支持壓縮的垃圾回收器會(huì)把所有的活對(duì)象搬遷到一起,然后將之前占用的內(nèi)存全部回收。不壓縮式的垃圾回收器只是回收內(nèi)存,這樣回收回來(lái)的內(nèi)存不可能是連續(xù)的,因此將會(huì)有較多的內(nèi)存碎片。較之壓縮式的垃圾回收,不壓縮式的垃圾回收回收內(nèi)存塊,而分配內(nèi)存時(shí)就會(huì)更慢,而且無(wú)法解決內(nèi)存碎片的問(wèn)題。復(fù)制式的垃圾回收會(huì)將所有可達(dá)對(duì)象復(fù)制到另一塊相同的內(nèi)存中,這種方式的優(yōu)點(diǎn)是垃圾回收過(guò)程不會(huì)產(chǎn)生內(nèi)存碎片,但缺點(diǎn)也很明顯,需要復(fù)制數(shù)據(jù)和額外的內(nèi)存。南京Java培訓(xùn)
復(fù)制:將堆內(nèi)分成兩個(gè)相同空間,從根(有向圖的起始節(jié)點(diǎn))開(kāi)始訪問(wèn)每一個(gè)關(guān)聯(lián)的柯達(dá)對(duì)象,將空間A的柯達(dá)對(duì)象全部復(fù)制到空間B,然后一次性回收整個(gè)空間A。對(duì)于復(fù)制算法而言,因?yàn)橹恍柙L問(wèn)所有的可達(dá)對(duì)象,將所有可達(dá)對(duì)象復(fù)制走之后就回收整個(gè)空間,完全不用理會(huì)那些不可達(dá)的對(duì)象,所以遍歷空間的成本較小,但需要巨大的復(fù)制成本和較多的內(nèi)存。南京Java軟件培訓(xùn)
不壓縮:標(biāo)記清除(mark-sweep),垃圾回收器從根開(kāi)始訪問(wèn)所有柯達(dá)對(duì)象,將它們標(biāo)記為可達(dá)狀態(tài),然后再遍歷一次整個(gè)內(nèi)存區(qū)域,把所有沒(méi)有標(biāo)記為可達(dá)的對(duì)象進(jìn)行回收處理。標(biāo)記清除無(wú)需進(jìn)行大規(guī)模的復(fù)制操作,而且內(nèi)存利用率高。但這些算法需要兩次遍歷堆內(nèi)存空間,遍歷的成本較大,因此造成應(yīng)用程序暫停的事件隨堆空間大小線性增大。而且垃圾回收回來(lái)的內(nèi)存往往是不連續(xù)的,因此整理后堆內(nèi)存里的碎片很多。
壓縮:標(biāo)記壓縮(mark-sweep-compact):這種方式充分利用上述兩種算法的優(yōu)點(diǎn),垃圾回收器先從根開(kāi)始訪問(wèn)所有可達(dá)對(duì)象,將它們標(biāo)記為可達(dá)狀態(tài)。接下來(lái)垃圾回收器會(huì)將這些活動(dòng)對(duì)象搬遷在一起,這個(gè)過(guò)程也被稱為內(nèi)存壓縮,然后垃圾回收機(jī)制再次回收那些不可達(dá)對(duì)象所占用的內(nèi)存空間,這樣就避免了回收產(chǎn)生的內(nèi)存碎片。
現(xiàn)行的垃圾回收器用分代的方式來(lái)采用不同的回收設(shè)計(jì)。分代的基本思路是根據(jù)對(duì)象生存事件的長(zhǎng)短,把堆內(nèi)存分為3個(gè)代:南京Java培訓(xùn)
Young;Old;Permanent。
垃圾回收器會(huì)根據(jù)不同代的特點(diǎn)采用不同的回收算法,從而充分利用各種回收算法的優(yōu)點(diǎn)。
分代回收的一個(gè)依據(jù)就是對(duì)象生存時(shí)間的長(zhǎng)短,然后再根據(jù)不同代采取不同的垃圾回收策略。采用分代策略基于兩個(gè)事實(shí):
絕大多數(shù)的對(duì)象不會(huì)被長(zhǎng)時(shí)間引用,這些對(duì)象在其Young期間就會(huì)被回收。南京Java培訓(xùn)
很老的對(duì)象(生存時(shí)間很長(zhǎng))和很新的對(duì)象(生存時(shí)間很短)之間很少存在相互引用的情況。
Young代
對(duì)于Young代采用復(fù)制算法只需遍歷那些處于可達(dá)狀態(tài)的對(duì)象,而且這些對(duì)象的數(shù)量較少,可復(fù)制成本也不大,因此可以充分發(fā)揮復(fù)制算法的優(yōu)點(diǎn)。Young代由1個(gè)Eden區(qū)和2個(gè)Survivor區(qū)構(gòu)成。絕大多數(shù)對(duì)象先分配到Eden區(qū)中,Survivor區(qū)中的對(duì)象都至少在Young代中經(jīng)歷過(guò)一次垃圾回收,所以這些對(duì)象在被轉(zhuǎn)移到Old代之前會(huì)先保留在Survivor空間中。同一時(shí)間2個(gè)Survivor空間中有一個(gè)用來(lái)保存對(duì)象,而另一個(gè)是空的,用來(lái)在下次垃圾回收時(shí)保存Young代中的對(duì)象。每次復(fù)制就是將Eden和第一個(gè)Survivor的可達(dá)對(duì)象復(fù)制到第2個(gè)Survivor區(qū),然后清空Eden與第1個(gè)Survivor區(qū)。Eden與 Survivor的比例通過(guò)-XX:SurvivorRatio附加選項(xiàng)來(lái)設(shè)定,默認(rèn)為32.如果Survivor太大會(huì)產(chǎn)生浪費(fèi),太小則會(huì)使一些Young代的對(duì)象提前進(jìn)入Old代。南京Java培訓(xùn)
Old代
如果Young代中對(duì)象經(jīng)過(guò)數(shù)次垃圾回收依然還沒(méi)有被回收掉,即這個(gè)對(duì)象經(jīng)過(guò)足夠長(zhǎng)的時(shí)間還處于可達(dá)狀態(tài),垃圾回收機(jī)制就會(huì)將這個(gè)對(duì)象轉(zhuǎn)移到Old代。Old代的空間比Yound代要大很多。
Old代的垃圾回收兩個(gè)特征:
1. Old代垃圾回收的執(zhí)行頻率無(wú)需太高,因?yàn)楹苌儆袑?duì)象會(huì)死掉。
2. 每次對(duì)Old代執(zhí)行垃圾回收需要更長(zhǎng)的時(shí)間來(lái)完成。南京Java培訓(xùn)
基于以上考慮,垃圾回收器通常會(huì)使用標(biāo)記壓縮算法。這種算法可以避免復(fù)制Old代的大量對(duì)象,而且Old代的對(duì)象不會(huì)很快死亡,回收過(guò)程不會(huì)大量地產(chǎn)生內(nèi)存碎片,因此相對(duì)比較劃算。
Permanent代
Permanent代主要用于裝載Class,方法等信息,默認(rèn)為64M,垃圾回收機(jī)制通常不會(huì)回收Permanent代中的對(duì)象。對(duì)于那些需要加載很多類的服務(wù)器程序,往往需要加大Permanent代內(nèi)存,否則可能因?yàn)閮?nèi)存不足而導(dǎo)致程序終止。對(duì)于像Hibernate,Spring這類喜歡AOP動(dòng)態(tài)生成類的框架,往往會(huì)生成大量的動(dòng)態(tài)代理類,因此需要更多的Permanent代內(nèi)存。如果遇到j(luò)ava.lang.OutOfMemoryError:PermGen space的錯(cuò)誤,這就是由Permanent代內(nèi)存耗盡所導(dǎo)致的錯(cuò)誤。南京Java培訓(xùn)
當(dāng)Young代的內(nèi)存將要用完的時(shí)候,垃圾回收機(jī)制會(huì)對(duì)Young代進(jìn)行垃圾回收,垃圾回收機(jī)制會(huì)采用較高的頻率對(duì)Young代進(jìn)行掃描和回收。因?yàn)檫@種回收的系統(tǒng)開(kāi)銷比較小,因此也被稱為次要回收(MINOR collection)。當(dāng)Old代的內(nèi)存將要用完時(shí),垃圾回收機(jī)制會(huì)進(jìn)行全回收,也就是對(duì)Young代和Old代都要進(jìn)行回收,此時(shí)回收成本就大得多了,因此也稱為主要回收(Major Collection)。