為什么要了解垃圾回收涉波?
當(dāng)需要排查各種內(nèi)存溢出、內(nèi)存泄漏問題胧瓜,當(dāng)垃圾收集成為系統(tǒng)達(dá)到更高并發(fā)量的瓶頸時(shí),就需要進(jìn)行必要的監(jiān)控和調(diào)節(jié)瘾晃。什么時(shí)候回收贷痪?
2.1 引用計(jì)數(shù)法:每當(dāng)一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加1蹦误;當(dāng)引用失效時(shí)劫拢,計(jì)數(shù)器減1;但無法解決對象循環(huán)引用的問題
2.2 可達(dá)性分析算法:對任何“活”的對象强胰,一定能最終追溯到其存活在堆棽詹祝或靜態(tài)存儲(chǔ)區(qū)之中的引用。即一定有一條引用鏈可以將一個(gè)對象連到GC Roots偶洋。
可以作為GC Roots的對象包括:虛擬機(jī)棧(本地變量表)中引用的對象熟吏;方法區(qū)中類靜態(tài)屬性引用的對象和常量引用的對象;本地方法棧中JNI(即Native方法)引用的對象。
-
怎么回收牵寺?
3.1 JAVA堆:新生代(Young)悍引、老年代(Old)
新聲代又被劃分為三個(gè)區(qū)域:Eden、From Survivor帽氓、To Survivor趣斤。
3.2 Minor GC 是發(fā)生在新生代中的垃圾收集動(dòng)作,所采用的是復(fù)制算法黎休。
Full GC 是發(fā)生在老年代的垃圾收集動(dòng)作浓领,所采用的是標(biāo)記-清除算法
當(dāng)對象在 Eden ( 包括一個(gè) Survivor 區(qū)域,這里假設(shè)是 from 區(qū)域 ) 出生后势腮,在經(jīng)過一次 Minor GC 后联贩,如果對象還存活,并且能夠被另外一塊 Survivor 區(qū)域所容納( 上面已經(jīng)假設(shè)為 from 區(qū)域捎拯,這里應(yīng)為 to 區(qū)域泪幌,即 to 區(qū)域有足夠的內(nèi)存空間來存儲(chǔ) Eden 和 from 區(qū)域中存活的對象 ),則使用復(fù)制算法將這些仍然還存活的對象復(fù)制到另外一塊 Survivor 區(qū)域 ( 即 to 區(qū)域 ) 中玄渗,然后清理所使用過的 Eden 以及 Survivor 區(qū)域 ( 即 from 區(qū)域 )座菠,并且將這些對象的年齡設(shè)置為1,以后對象在 Survivor 區(qū)每熬過一次 Minor GC藤树,就將對象的年齡 + 1浴滴,當(dāng)對象的年齡達(dá)到某個(gè)值時(shí) ( 默認(rèn)是 15 歲,可以通過參數(shù) -XX:MaxTenuringThreshold 來設(shè)定 )岁钓,這些對象就會(huì)成為老年代升略。
但這也不是一定的,對于一些較大的對象 ( 即需要分配一塊較大的連續(xù)內(nèi)存空間 ) 則是直接進(jìn)入到老年代屡限。
在辨別出需要回收的對象后品嚣,JAVA虛擬機(jī)在回收時(shí)有不同做法。
“停止-復(fù)制”钧大。先暫停程序的運(yùn)行翰撑,然后將所有存活的對象從當(dāng)前堆一個(gè)挨著一個(gè)復(fù)制到另一個(gè)堆。
采用這種方式時(shí)啊央,新堆將保持緊湊排列眶诈。但在效率上卻存在問題,第一瓜饥,需要多一倍的維護(hù)空間逝撬;第二,當(dāng)程序進(jìn)入穩(wěn)定狀態(tài)后乓土,可能只會(huì)產(chǎn)生少量垃圾宪潮,甚至沒有垃圾溯警,此時(shí),復(fù)制式回收器仍然會(huì)將所有內(nèi)存自一處復(fù)制到另一處狡相。
“標(biāo)記-清掃”梯轻。在尋找存活對象的過程中,對每一個(gè)存活對象打標(biāo)記谣光。當(dāng)標(biāo)記工作完成后檩淋,沒有被標(biāo)記的對象將被清理。
采用這種方式萄金,在效率上有了很大的提升,但清理后剩下的堆空間是不連續(xù)的媚朦。
實(shí)際上氧敢,JAVA虛擬機(jī)在清理垃圾時(shí)采用一種“自適應(yīng)”技術(shù)。
JAVA虛擬機(jī)會(huì)對回收操作進(jìn)行監(jiān)控询张,當(dāng)對象很穩(wěn)定孙乖,垃圾很少,回收效率降低時(shí)份氧,切換到“標(biāo)記-清掃”方式唯袄。當(dāng)堆空間出現(xiàn)很多碎片時(shí),就會(huì)切換回“停止-復(fù)制”方式蜗帜。