一文吃透JVM分代回收機(jī)制
為什么要分代
分代的垃圾回收策略,是基于這樣一個(gè)事實(shí):不同的對(duì)象的生命周期是不一樣的。因此,不同生命周期的對(duì)象可以采取不同的收集方式,以便提高回收效率。
在Java程序運(yùn)行的過程中,會(huì)產(chǎn)生大量的對(duì)象,其中有些對(duì)象是與業(yè)務(wù)信息相關(guān),比如Http請(qǐng)求中的Session對(duì)象、線程、Socket連接,這類對(duì)象跟業(yè)務(wù)直接掛鉤,因此生命周期比較長(zhǎng)。但是還有一些對(duì)象,主要是程序運(yùn)行過程中生成的臨時(shí)變量,這些對(duì)象生命周期會(huì)比較短,比如:String對(duì)象,由于其不變類的特性,系統(tǒng)會(huì)產(chǎn)生大量的這些對(duì)象,有些對(duì)象甚至只用一次即可回收。
試想,在不進(jìn)行對(duì)象存活時(shí)間區(qū)分的情況下,每次垃圾回收都是對(duì)整個(gè)堆空間進(jìn)行回收,花費(fèi)時(shí)間相對(duì)會(huì)長(zhǎng),同時(shí),因?yàn)槊看位厥斩夹枰闅v所有存活對(duì)象,但實(shí)際上,對(duì)于生命周期長(zhǎng)的對(duì)象而言,這種遍歷是沒有效果的,因?yàn)榭赡苓M(jìn)行了很多次遍歷,但是他們依舊存在。因此,分代垃圾回收采用分治的思想,進(jìn)行代的劃分,把不同生命周期的對(duì)象放在不同代上,不同代上采用最適合它的垃圾回收方式進(jìn)行回收。
如何分代
圖片
如圖所示:
虛擬機(jī)中的共劃分為三個(gè)代:年輕代(Young Generation)、年老點(diǎn)(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java類的類信息,與垃圾收集要收集的Java對(duì)象關(guān)系不大。年輕代和年老代的劃分是對(duì)垃圾收集影響比較大的。
對(duì)象分類
這種算法并不是一種新的算法,而是根據(jù)對(duì)象的存活周期的不同而將內(nèi)存分為幾塊,分別為新生代、老年代和永久代。
新生代:朝生夕滅的對(duì)象(例如:方法的局部變量等)。
老年代:存活得比較久,但還是要死的對(duì)象(例如:緩存對(duì)象、單例對(duì)象等)。
永久代:對(duì)象生成后幾乎不滅的對(duì)象(例如:加載過的類信息)。
內(nèi)存區(qū)域
回想一下之前jvm對(duì)內(nèi)存的劃分,我們可能就已經(jīng)猜到了,新生代和老年代都在java堆,永久代在方法區(qū)。
java堆對(duì)象的回收
現(xiàn)在,我們來看看分代收集算法是如何針對(duì)堆內(nèi)存進(jìn)行回收的。
新生代:采用復(fù)制算法,新生代對(duì)象一般存活率較低,因此可以不使用50%的內(nèi)存作為空閑,一般的,使用兩塊10%的內(nèi)存
作為空閑和活動(dòng)區(qū)間,而另外80%的內(nèi)存,則是用來給新建對(duì)象分配內(nèi)存的。一旦發(fā)生GC,將10%的活動(dòng)區(qū)間與另外80%中存
活的對(duì)象轉(zhuǎn)移到10%的空閑區(qū)間,接下來,將之前90%的內(nèi)存全部釋放,以此類推,下面還是用一張圖來說明:
解釋下,堆大小=新生代+老年代,新生代與老年代的比例為1:2,新生代細(xì)分為一塊較大的Eden空間和兩塊較小的Survivor空間,分別被命名為from和to。
老年代:老年代中使用“標(biāo)記-清除”或者“標(biāo)記-整理”算法進(jìn)行垃圾回收,回收次數(shù)相對(duì)較少,每次回收時(shí)間比較長(zhǎng)。
方法區(qū)對(duì)象回收
永久代指的是虛擬機(jī)內(nèi)存中的方法區(qū),永久代垃圾回收比較少,效率也比較低,但也必須進(jìn)行垃圾回收,否則永久代內(nèi)存
不夠用時(shí)仍然會(huì)拋出OutOfMemoryError異常。永久代也使用“標(biāo)記-清除”或者“標(biāo)記-整理”算法進(jìn)行垃圾回收。
什么情況下觸發(fā)垃圾回收
由于對(duì)象進(jìn)行了分代處理,因此垃圾回收區(qū)域、時(shí)間也不一樣。GC有兩種類型:Scavenge GC和Full GC。
Scavenge GC
一般情況下,當(dāng)新對(duì)象生成,并且在Eden申請(qǐng)空間失敗時(shí),就會(huì)觸發(fā)Scavenge GC,對(duì)Eden區(qū)域進(jìn)行GC,清除非存活對(duì)象,并且把尚且存活的對(duì)象移動(dòng)到Survivor區(qū)。然后整理Survivor的兩個(gè)區(qū)。這種方式的GC是對(duì)年輕代的Eden區(qū)進(jìn)行,不會(huì)影響到年老代。因?yàn)榇蟛糠謱?duì)象都是從Eden區(qū)開始的,同時(shí)Eden區(qū)不會(huì)分配的很大,所以Eden區(qū)的GC會(huì)頻繁進(jìn)行。因而,一般在這里需要使用速度快、效率高的算法,使Eden去能盡快空閑出來。
Full GC
對(duì)整個(gè)堆進(jìn)行整理,包括Young、Tenured和Perm。Full GC因?yàn)樾枰獙?duì)整個(gè)對(duì)進(jìn)行回收,所以比Scavenge GC要慢,因此應(yīng)該盡可能減少Full GC的次數(shù)。在對(duì)JVM調(diào)優(yōu)的過程中,很大一部分工作就是對(duì)于FullGC的調(diào)節(jié)。有如下原因可能導(dǎo)致Full GC: