在什么時候:
首先需要知道,GC又分為minor GC 和 Full GC(major GC)。Java堆內存分為新生代和老年代,新生代
中又分為1個eden區和兩個Survior區域。
一般情況下,新創建的對象都會被分配到eden區,這些對象經過一個minor gc后仍然存活將會被移動到
Survior區域中,對象在Survior中沒熬過一個Minor GC,年齡就會增加一歲,當他的年齡到達一定程度時,
就會被移動到老年代中。
當eden區滿時,還存活的對象將被復制到survior區,當一個survior區滿時,此區域的存活對象將被復制到另外一個
survior區,當另外一個也滿了的時候,從前一個Survior區復制過來的并且此時還存活的對象,將可能被復制到老年代
因為年輕代中的對象基本都是朝生夕死(80%以上),所以年輕代的垃圾回收算法使用的是復制算法,
復制算法的基本思想是將內存分為兩塊,每次只有其中一塊,當這一塊內存使用完,就將還活著的對象復制到
另一塊上面。復制算法不會產生內存碎片。
在GC開始的時候,對象只會存在于eden區,和名為“From”的Survior區,Survior區“to”是空的。緊接著GC
eden區中所有存活的對象都會被復制到“To”,而在from區中,仍存活的對象會根據他們的年齡值來決定去向,
年齡到達一定只的對象會被復制到老年代,沒有到達的對象會被復制到to survior中,經過這次gc后,eden區和from
survior區已經被清空。這個時候,from和to會交換他們的角色,也就是新的to就是上次GC前的from
Minor GC:從年輕代回收內存
當jvm無法為一個新的對象分配空間時會觸發Minor GC,比如當Eden區滿了。
當內存池被填滿的時候,其中的內容全部會被復制,指針會從0開始跟蹤空閑內存。Eden和Survior區不存在內存碎片
寫指針總是停留在所使用內存池的頂部。執行minor操作時不會影響到永久代,從永久帶到年輕代的引用被當成
GC roots,從年輕代到永久代的引用在標記階段被直接忽略掉(永久代用來存放java的類信息)。如果eden區域中大部分
對象被認為是垃圾,永遠也不會復制到Survior區域或者老年代空間。如果正好相反,eden區域大部分新生對象不符合GC
條件,Minor GC執行時暫停的線程時間將會長很多。Minor may call “stop the world”;
Full GC:是清理整個堆空間包括年輕代和老年代。
那么對于Minor GC的觸發條件:大多數情況下,直接在eden區中進行分配。如果eden區域沒有足夠的空間,
那么就會發起一次Minor GC;對于FullGC的觸發條件:如果老年代沒有足夠的空間,那么就會進行一次FullGC
在發生MinorGC之前,虛擬機會先檢查老年代最大可利用的連續空間是否大于新生代所有對象的總空間。
如果大于則進行Minor GC,如果小于則看HandlePromotionFailure設置是否是允許擔保失敗(不允許則直接FullGC)
如果允許,那么會繼續檢查老年代最大可利用的連續空間是否大于歷次晉升到老年代對象的平均大小,如果大于
則嘗試minor gc (如果嘗試失敗也會觸發Full GC),如果小于則進行Full GC。
但是,具體什么時候執行,這個是由系統來進行決定的,是無法預測的。
對什么東西:
主要根據可達性分析算法,如果一個對象不可達,那么就是可以回收的,如果一個對象可達,那么這個對象就不可以回收,
對于可達性分析算法,它是通過一系列稱為“GC Roots”的對象最為起始點,當一個對象GC Roots沒有任何引用鏈相接的時候,
那么這個對象就是不可達,就可以被回收。
做了什么事情:
主要做了清理對象,整理內存的工作。Java堆分為新生代和老年代,采用了不同的回收方式。
例如新生代采用了復制算法,老年代采用了標記整理法。在新生代中,分為一個ede區域和兩個Survior
區域,真正使用的是一個eden區域,和一個Survior區域,GC的時候,會把存活的對象放入到另一個Survior區域中,
然后再把這個eden區域和Survior區域清除。那么對于老年代,采用的是標記整理發,首先標記出存活對象,
然后在移動到一段。這樣有利于減少內存碎片。
標記:標記的過程其實就是,遍歷所有gc root 然后將所有gc root 可達的對象標記為存活對象
清除:清除的過程中將遍歷堆中所有的對象,將沒有標記的對象全部清除掉
主要缺點:標記和清除過程效率不高,標記清除之后會產生大量不連續的內存碎片
但是,老年代中因為對象存活率高,沒有額外空間對他進行分配擔保,就必須使用標記整理算法
標記整理算法 標記操作和“標記-清除”算法一致,后續操作不只是直接清理對象,而是在清理
無用對象完成后讓所有存活的對象都向一段移動,并更新其引用對象的指針
主要缺點:在標記清除的基礎上還需要進行對象的移動,成本相對比較高,成本相對較高,好處是不會產生內存碎片。