1:對(duì)象的回收前期工作
finalize()方法的調(diào)用:
當(dāng)一個(gè)對(duì)象不再使用時(shí)會(huì)被java虛擬機(jī)銷(xiāo)毀,并回收其所占內(nèi)存。java中并沒(méi)有c++中類(lèi)似析構(gòu)函數(shù)這樣的方法,當(dāng)調(diào)用對(duì)象的delete()方法時(shí),在析構(gòu)函數(shù)中做一些資源釋放的工作。
1)在java中,當(dāng)我們調(diào)用System.gc()時(shí),Java垃圾回收并不會(huì)立即執(zhí)行或者根本不會(huì)執(zhí)行,這主要取決于java虛擬機(jī)此時(shí)對(duì)程序運(yùn)行狀態(tài)的一個(gè)評(píng)測(cè)。
2)當(dāng)一個(gè)對(duì)象被垃圾回收器回收前,會(huì)首先會(huì)調(diào)用其finalize()方法,在這個(gè)方法中我們可以做一些與內(nèi)存無(wú)關(guān)的內(nèi)存回收工作,例如對(duì)文件的訪(fǎng)問(wèn)、socket的連接斷開(kāi)、jni資源的釋放,最后還要調(diào)用super.finalize()。
2:垃圾回收器的工作方式:“標(biāo)記-清掃”和“停止-復(fù)制”
1)java中所有對(duì)象都是在堆內(nèi)存中創(chuàng)建的
2)堆內(nèi)存的分配,由于java垃圾回收器的介入,使得對(duì)象的內(nèi)存分配效率提高很多
標(biāo)記-清掃
在進(jìn)行一次gc的過(guò)程中,java虛擬機(jī)會(huì)掃描堆內(nèi)存和靜態(tài)存儲(chǔ)區(qū)的所有對(duì)象,并對(duì)其進(jìn)行“存活”狀態(tài)標(biāo)記,掃描完成后(掃描過(guò)程中不清理對(duì)象,掃描完成后才開(kāi)始清理),將沒(méi)有被標(biāo)記為“存活”狀態(tài)的對(duì)象進(jìn)行銷(xiāo)毀(不會(huì)發(fā)生復(fù)制“存活”對(duì)象到另一個(gè)堆區(qū)域的動(dòng)作),也就是所謂GC ROOT無(wú)法到達(dá)的對(duì)象,此時(shí)會(huì)存在很多不連續(xù)的堆空間,如果垃圾回收器希望得到連續(xù)的空間,就需要重新整理“存活對(duì)象”。此過(guò)程進(jìn)行前,程序需要暫停。
停止-復(fù)制
垃圾清理過(guò)程中,死對(duì)象清理的同時(shí),會(huì)將“活對(duì)象”復(fù)制到另一個(gè)堆中,以使所有“活對(duì)象”擁有連續(xù)的內(nèi)存空間,這樣為新對(duì)象的內(nèi)存分配提高了效率,但也帶來(lái)了問(wèn)題。第一需要兩個(gè)堆內(nèi)存區(qū)域,“活對(duì)象”需要在這兩個(gè)區(qū)域進(jìn)行復(fù)制,這樣內(nèi)存開(kāi)銷(xiāo)太大;第二由于對(duì)象指向的內(nèi)存發(fā)生了變化,那么此時(shí)就需要對(duì)對(duì)象進(jìn)行地址修改。此過(guò)程進(jìn)行前,程序需要暫停。
隨著java虛擬機(jī)的新技術(shù)出現(xiàn),對(duì)“死對(duì)象”和“活對(duì)象”內(nèi)存有了改善。此時(shí)虛擬機(jī)會(huì)按塊分配內(nèi)存,當(dāng)一個(gè)對(duì)象被銷(xiāo)毀后,此塊就會(huì)被標(biāo)記為廢棄,此時(shí)就可以直接往廢棄塊中拷貝對(duì)象,這樣就不必分配兩個(gè)單獨(dú)的堆區(qū)域,同時(shí)垃圾回收器也會(huì)在回收之后對(duì)內(nèi)存進(jìn)行整理。
總結(jié)
針對(duì)以上“標(biāo)記-清掃”和“停止-復(fù)制”,java虛擬機(jī)采用一種被稱(chēng)為“自適應(yīng)”的方式工作。當(dāng)內(nèi)存充足,gc頻率低,程序處于一種比較穩(wěn)定的狀態(tài)時(shí),此時(shí)就會(huì)采用“標(biāo)記-清掃”模式。但是在長(zhǎng)期的gc之后,如果碎片化比較嚴(yán)重(因?yàn)椤皹?biāo)記-清理”模式,只是釋放"死對(duì)象"堆內(nèi)存,并未對(duì)區(qū)域進(jìn)行整理),此時(shí)就會(huì)切換到“停止-復(fù)制”模式。