標記清除算法:
1,標記階段:找到所有可以訪問的對象,做個標記
2,清除階段:遍歷堆,找到未做標記的對象,進行清除
標記和清除的效率不高,尤其是要掃描的對象比較多的時候 ,(一般用於老年代,因為老年代回收的頻率不高)
缺點: 會造成內存碎片(會導致明明有內存空間,但是由於不連續,申請稍微大一些的對象無法做到)
優點:可以解決循環依賴的問題
復制算法:
如果jvm使用了coping算法,一開始就會將可用內存分為兩塊,from域和to域, 每次只是使用from域,to域則空閑着。當from域內存不夠了,開始執行GC操作,這個時候,會把from域存活的對象拷貝到to域,然后直接把from域進行內存清理。
coping算法一般是使用在新生代中,因為新生代中的對象一般都是朝生夕死的,存活對象的數量並不多,這樣使用coping算法進行拷貝時效率比較高。jvm將Heap 內存划分為新生代與老年代,又將新生代划分為Eden(伊甸園) 與2塊Survivor Space(幸存者區) ,然后在Eden –>Survivor Space 以及From Survivor Space 與To Survivor Space 之間實行Copying 算法。 不過jvm在應用coping算法時,並不是把內存按照1:1來划分的,這樣太浪費內存空間了。一般的jvm都是8:1。也即是說,Eden區:From區:To區域的比例是
始終有90%的空間是可以用來創建對象的,而剩下的10%用來存放回收后存活的對象。
1、當Eden區滿的時候,會觸發第一次young gc,把還活着的對象拷貝到Survivor From區;當Eden區再次觸發young gc的時候,會掃描Eden區和From區域,對兩個區域進行垃圾回收,經過這次回收后還存活的對象,則直接復制到To區域,並將Eden和From區域清空。
2、當后續Eden又發生young gc的時候,會對Eden和To區域進行垃圾回收,存活的對象復制到From區域,並將Eden和To區域清空。
3、可見部分對象會在From和To區域中復制來復制去,如此交換15次(由JVM參數MaxTenuringThreshold決定,這個參數默認是15),最終如果還是存活,就存入到老年代
S0 和 S1一定有一個是空的,目的就是為了存放下一次的復制。
注意: 萬一存活對象數量比較多,那么To域的內存可能不夠存放,這個時候會借助老年代的空間。
優缺點:
優點:在存活對象不多的情況下,性能高,能解決內存碎片和java垃圾回收算法之-標記清除 中導致的引用更新問題。
缺點: 會造成一部分的內存浪費。不過可以根據實際情況,將內存塊大小比例適當調整;如果存活對象的數量比較大,coping的性能會變得很差。
標記壓縮算法:
將堆內存里面的對象先排序,后清除
標記清除算法和標記壓縮算法非常相同,但是標記壓縮算法在標記清除算法之上解決內存碎片化
排序方法:
任意順序 : 即不考慮原先對象的排列順序,也不考慮對象之間的引用關系,隨意移動對象;
線性順序 : 考慮對象的引用關系,例如a對象引用了b對象,則盡可能將a和b移動到一塊;
滑動順序 : 按照對象原來在堆中的順序滑動到堆的一端
優缺點:
優點:解決內存碎片問題,缺點壓縮階段,由於移動了可用對象,需要去更新引用。
Minor GC & Major GC
新生代 GC(Minor GC):指發生在新生代的垃圾收集動作,因為 Java 對象大多都具備朝生夕滅的特性,所以 Minor GC 非常頻繁,一般回收速度也比較快。
老年代 GC(Major GC / Full GC):指發生在老年代的 GC,出現了 Major GC,經常會伴隨至少一次的 Minor GC(但非絕對的,在 ParallelScavenge 收集器的收集策略里就有直接進行 Major GC 的策略選擇過程) 。MajorGC 的速度一般會比 Minor GC 慢 10倍以上。
Minor GC觸發機制:
當年輕代滿時就會觸發Minor GC,這里的年輕代滿指的是Eden代滿,Survivor滿不會引發GCFull GC觸發機制:
當年老代滿時會引發Full GC,Full GC將會同時回收年輕代、年老代,當永久代滿時也會引發Full GC,會導致Class、Method元信息的卸載其中Minor GC如下圖所示
虛擬機給每個對象定義了一個對象年齡(Age)計數器。如果對象在 Eden 出生並經過第一次 Minor GC 后仍然存活,並且能被 Survivor 容納的話,將被移動到 Survivor 空間中,並將對象年齡設為 1。對象在 Survivor 區中每熬過一次 Minor GC,年齡就增加 1 歲,當它的年齡增加到一定程度(默認為 15 歲)時,就會被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過參數 -XX:MaxTenuringThreshold (閾值)來設置。
分代算法:
這種算法,根據對象的存活周期的不同將內存划分成幾塊,新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集算法。可以用抓重點的思路來理解這個算法。
新生代對象朝生夕死,對象數量多,只要重點掃描這個區域,那么就可以大大提高垃圾收集的效率。另外老年代對象存儲久,無需經常掃描老年代,避免掃描導致的開銷。
新生代
在新生代,每次垃圾收集器都發現有大批對象死去,只有少量存活,采用復制算法,只需要付出少量存活對象的復制成本就可以完成收集;可以參看我之前寫的java垃圾回收算法之-coping復制
老年代
而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須“標記-清除-壓縮”算法進行回收。參看java垃圾回收算法之-標記_清除壓縮
新創建的對象被分配在新生代,如果對象經過幾次回收后仍然存活,那么就把這個對象划分到老年代。
老年代區存放Young區Survivor滿后觸發minor GC后仍然存活的對象,當Eden區滿后會將存活的對象放入Survivor區域,如果Survivor區存不下這些對象,GC收集器就會將這些對象直接存放到Old區中,如果Survivor區中的對象足夠老,也直接存放到Old區中。如果Old區滿了,將會觸發Full GC回收整個堆內存