目錄
一、如何判斷一個對象是否存活
1.引用計數(shù)法
2.可達性分析法
二、垃圾回收算法
1.標記清除法
2.復制算法
3.標記整理法
4.分代算法
具體流程
注意事項
空間分配擔保原則
總結
一、如何判斷一個對象是否存活
1.引用計數(shù)法
給每一個對象設置一個引用計數(shù)器,當有一個地方引用該對象的時候,引用計數(shù)器就+1,引用失效時,引用計數(shù)器就-1;當引用計數(shù)器為0的時候,就說明這個對象沒有被引用,也就是垃圾對象,等待回收; 缺點:無法解決循環(huán)引用的問題,當A引用B,B也引用A的時候,此時AB對象的引用都不為0,此時也就無法垃圾回收,所以一般主流虛擬機都不采用這個方法。
循環(huán)引用問題:
一個類:
Class Person{
?Person p;
}?
一個方法:
void method(){
? ? Person s = new Person();? ?//此時該對象的引用數(shù)為1
? ?s.p = s;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //此時該對象的引用數(shù)位2
}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //方法結束,引用 s? 的生命結束,該對象的引用數(shù)位1
//此時該對象還被一個引用指向,但是我們無法拿到這個對象了,因為 引用 s 已經“死”了
2.可達性分析法
從一個被稱為GC Roots的對象向下搜索,如果一個對象到GC Roots沒有任何引用鏈相連接時,說明此對象不可用,在java中可以作為GC Roots的對象有以下幾種:
1.虛擬機棧中引用的對象
2.方法區(qū)類靜態(tài)屬性引用的變量
3.方法區(qū)常量池引用的對象
4.本地方法棧JNI引用的對象
可達性圖如下所示:
二、垃圾回收算法
1.標記清除法
第一步:利用可達性去遍歷內存,把存活對象和垃圾對象進行標記;第二步:在遍歷一遍,將所有標記的對象回收掉;特點:效率不行,標記和清除的效率都不高;標記和清除后會產生大量的不連續(xù)的空間分片,可能會導致之后程序運行的時候需分配大對象而找不到連續(xù)分片而不得不觸發(fā)一次GC。
2.復制算法
"復制"算法是為了解決"標記-清理"的效率問題。它將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這塊內存需要進行垃圾回收時,會將此區(qū)域還存活著的對象復制到另一塊上面,然后再把已經使用過的內存區(qū)域一次清理掉。這樣做的好處是每次都是對整個半?yún)^(qū)進行內存回收,內存分配時也就不需要考慮內存碎片等復雜情況,只需要移動堆頂指針,按順序分配即可。此算法實現(xiàn)簡單,運行高效。復制收集算法在對象存活率較高時會進行比較多的復制操作,效率會變低。
3.標記整理法
4.分代算法

具體流程
1.對象優(yōu)先在Eden分配。當 eden 區(qū)沒有足夠空間進行分配時,虛擬機將發(fā)起一次 Minor GC。
2.在 Eden 區(qū)執(zhí)行了第一次 GC 之后,存活的對象會被移動到?form 分區(qū);
3.Eden 區(qū)再次 GC,這時會采用復制算法,將 Eden 和 from 區(qū)一起清理,存活的對象會被復制到 to 區(qū);? ??
4.當后續(xù)Eden又發(fā)生Minor GC的時候,會對Eden和 to 區(qū)進行垃圾回收,存活的對象復制到 from 區(qū),并將Eden 和 to 區(qū)清空。
5.部分對象會在 from 和 to 區(qū)中來回的復制,如此的交換15次(由JVM參數(shù) Max Tenuring Threshold 決定,默認是15),最終如果還是存活,就存入老年代。
6.Survivor 區(qū)內存不足會發(fā)生擔保分配,超過指定大小的對象可以直接進入老年代(此時如果老年代的內存大小,小于對象的大小,可能會發(fā)生一次 Full GC,后面有提到)。
6.老年代滿了而無法容納更多的對象,Minor GC 之后通常就會進行Full GC,F(xiàn)ull GC 清理整個內存堆 –?包括年輕代和老年代。
注意事項
新生代:對于一般創(chuàng)建的對象都會進入。
老年代:對于大對象為了避免為大對象分配內存時由于分配擔保機制帶來的復制而降低效率;或者經過N次(一般是默認15次)的垃圾回收依然存活下來的對象;從新生代移動到老年代。
Minor GC、?Major GC、Full GC 的關系:?
1.Minor GC 又稱為新生代GC:指的是發(fā)生在新生代的垃圾回收。因為Java對象大多都具備朝生夕滅的特性,因此Minor GC(采用的是復制算法)非常頻繁,一般回收速度也比較的塊。
2.Major GC 又稱為老年代GC,一般的老年代GC,總是由于某次Minor GC 引起的,所以 Major GC 發(fā)生的時候也是 Full GC,可以看作他兩個等效。
空間分配擔保原則
如果YougGC時新生代有大量對象存活下來,而 survivor 區(qū)放不下了,這時必須轉移到老年代中,但這時發(fā)現(xiàn)老年代也放不下這些對象了,那怎么處理呢?其實JVM有一個老年代空間分配擔保機制來保證對象能夠進入老年代。
在執(zhí)行每次 YoungGC 之前,JVM會先檢查老年代最大可用連續(xù)空間是否大于新生代所有對象的總大小。因為在極端情況下,可能新生代 YoungGC 后,所有對象都存活下來了,而 survivor 區(qū)又放不下,那可能所有對象都要進入老年代了。這個時候如果老年代的可用連續(xù)空間是大于新生代所有對象的總大小的,那就可以放心進行 YoungGC。但如果老年代的內存大小是小于新生代對象總大小的,那就有可能老年代空間不夠放入新生代所有存活對象,這個時候JVM就會先檢查 -XX:HandlePromotionFailure 參數(shù)是否允許擔保失敗,如果允許,就會判斷老年代最大可用連續(xù)空間是否大于歷次晉升到老年代對象的平均大小(就是每次從新生代到老年代的對象的平均大小),如果大于,將嘗試進行一次YoungGC,盡快這次YoungGC是有風險的。如果小于,或者 -XX:HandlePromotionFailure 參數(shù)不允許擔保失敗,這時就會進行一次 Full GC。
在允許擔保失敗并嘗試進行YoungGC后,可能會出現(xiàn)三種情況:
1.YoungGC后,存活對象小于survivor大小,此時存活對象進入survivor區(qū)中。
2. YoungGC后,存活對象大于survivor大小,但是小于老年大可用空間大小,此時直接進入老年代。
3. YoungGC后,存活對象大于survivor大小,也大于老年代可用空間大小,老年代也放不下這些對象了,此時就會發(fā)生“Handle Promotion Failure”,就觸發(fā)了 Full GC。如果 Full GC后,老年代還是沒有足夠的空間,此時就會發(fā)生OOM內存溢出了。文章來源:http://www.zghlxwxcb.cn/news/detail-445945.html
總結
加油偶~~文章來源地址http://www.zghlxwxcb.cn/news/detail-445945.html
到了這里,關于java---垃圾回收算法(GC)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!