GC機制分析

最近看了很多關於GC機制的文章,感覺沒有找到十分符合我的文章,於是拿起《深入理解Java虛擬機》這本書自己來寫篇分析文章,方便自己理解,也方便分享給大家一起學習。

這個機制可以分解爲三個問題來看待:GC是在什麼時候,對什麼東西,做了什麼事情?

第一個問題:GC是在什麼時候?

在程序空閒或者堆內存滿的時候就會觸發GC。
HotSpot JVM把年輕代分爲了三部分:1個Eden區和2個Survivor區(分別叫from和to)。默認比例爲8:1,爲啥默認會是這個比例,接下來我們會聊到。一般情況下,新創建的對象都會被分配到Eden區(一些大對象特殊處理),這些對象經過第一次Minor GC後,如果仍然存活,將會被移到Survivor區。對象在Survivor區中每熬過一次Minor GC,年齡就會增加1歲,當它的年齡增加到一定程度時,就會被移動到年老代中。

/**
*解釋:爲了做到這一點,虛擬機給每個對象定義了一個對象年齡計數器,如果對象在Eden出生並經過一次Minor GC後仍然存活,並且能夠被Survivor區所容納,將被移動到Survivor區中,並且對象年齡設爲一。對象在Survivor區中沒熬過一次Minor GC,年齡增加一歲,當增加到一定程度時(默認是15),會被晉升到老年代中,對象的閾值可以通過參數設置
*/

因爲年輕代中的對象基本都是朝生夕死的(80%以上),所以在年輕代的垃圾回收算法使用的是複製算法,複製算法的基本思想就是將內存分爲兩塊,每次只用其中一塊,當這一塊內存用完,就將還活着的對象複製到另外一塊上面。複製算法不會產生內存碎片。
在GC開始的時候,對象只會存在於Eden區和名爲“From”的Survivor區,Survivor區“To”是空的。緊接着進行GC,Eden區中所有存活的對象都會被複制到“To”,而在“From”區中,仍存活的對象會根據他們的年齡值來決定去向。年齡達到一定值(年齡閾值,可以通過-XX:MaxTenuringThreshold來設置)的對象會被移動到年老代中,沒有達到閾值的對象會被複制到“To”區域。經過這次GC後,Eden區和From區已經被清空。這個時候,“From”和“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎樣,都會保證名爲To的Survivor區域是空的。Minor GC會一直重複這樣的過程,直到“To”區被填滿,“To”區被填滿之後,會將所有對象移動到年老代中。

/**
*解釋:
1. 動態對象年齡判定
虛擬機並不是永遠的要求對象的年齡必須達到設置的值才能晉升老年代,如果在Survivor區中相同年齡所有對象大小的總和大於Survivor區的一半,年齡大於或等於該年齡的對象就可以直接進入老年代。
2. 空間分配擔保
發生Minor GC前,虛擬機會先檢查老年代最大可用的連續空間是否大於新生代所有對象總空間,如果成立,則可以進行Minor GC。如果不成立,則會查看設置裏是否設置允許擔保失敗,如果允許,則會繼續檢查老年代最大可用的連續空間是否大於歷次晉升老年代的對象平均大小,如果大於,則進行一次Minor GC,如果小於或者設置是不允許擔保失敗,則會改爲進行一次Full GC。
大部分情況下都會設置允許擔保失敗,避免Full GC過於頻繁。
*/

這裏寫圖片描述

第二個問題:什麼對象會被GC?

一、引用計數法
給對象添加一個引用計數器,每當有一個地方引用他時,計數器值就加一,當引用失效時,計數器值就減1; 任何計數器值爲0的對象就是不可以被使用的。
但是這種算法有問題,如果當兩個對象相互引用,而再無其他任何引用時,GC機制便無法清除他們。
二、可達性分析算法
當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。

引用:
強引用,只要引用存在,則不會被GC清理
軟引用,當將要發生內存溢出之前,會被進行回收清理
弱引用,只能生存到下一次GC發生之前
虛引用,對生存時間無任何影響。

第三個問題:GC幹什麼?

如果對象在進行可達性分析後發現沒有與GC Roots相連接的引用鏈,那他將會被第一次標記並進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法,如果需要執行,那麼他會被放入F-Queue隊列中,虛擬機會有一個Finalizer線程去執行它。之後GC會對F-Queue中的對象進行第二次小規模標記,如果對象在finalize()方法中成功拯救自己,比如重新與引用鏈上的任何一個對象建立關聯。那麼則會在第二次標記時被移出即將回收的集合。

爲什麼會有兩次標記?
因爲CMS收集器有兩個重要的步驟,初始標記與重新標記。CMS是標記-清除
G1是標記-整理
ps:後面還有好多關於GC收集器的內容就不寫了,打的手累。。。想學習的可以看看《深入理解Java虛擬機》這本書。

減少 主GC 開銷措施

1.不要顯示的調用System.gc()

雖然此函數建議JVM進行主GC,但經常會觸發主GC的執行,增加了間歇性停 頓的次數

2.減少臨時對象的使用

臨時對象在跳出函數調用後會成爲垃圾,少用臨時對象就相當於減少垃圾的產生從而延長了堆內存不足事件發生的時間,減少主GC的機會

3.對象不用時顯示的置爲NULL

爲NULL的對象都會被作爲垃圾處理,有利垃圾回收器判定垃圾,提高GC效 率

4.儘量使用StringBuffer而不是String進行累加字符串

5.能用int,long基本數據類型而不使用Integer,Long對對象

6.少使用靜態對象變量

靜態變量屬於全局變量,不會被垃圾回收器回收

7.分散對象創建和刪除時間

短時間創建大量對象會需要大量內存空間,JVM只能調用主GC回收內存,整合內存碎片。短時間內刪除大量對象產生大量垃圾對象,增加了下次創建新對象時強制主GC的機會

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章