JAVA垃圾回收機制GC(Garbage Collection)
工作面試老夥伴之java垃圾回收機制
JAVA垃圾回收機制,英文名(Garbage Collection),又簡稱GC,是很多面試官非常青睞的一個考點,今天謹以此文章給自己做一次系統的複習,也給正在學習這個知識點的筒子們來點參考,如果有發現講的不恰當(不錯)的地方,多請指教(收藏)哈O(∩_∩)O。
記錄目前主要爲自己加深理解,後期會加入自己畫的配圖供大家理解。
什麼是GC,爲什麼要GC(我的明星老姐)
我的姐姐小美是個明星,除了日常的工作以外,她的愛好就是打扮自己,以至於每次回家她都會拎着大包小包的新衣服回家(創建對象),然後在衣帽間(新生代)進行各種的試穿搭配(引用對象)瘋狂拍照咔咔咔,有些衣服她可能就試個一次就換下了,拍的照片也不會保存,碰到好看的她就會經常穿着,並保存着她精修的照片(#.#),但是嘞她有個不好的習慣,她懶得去整理那些不愛穿的衣服,久而久之,衣帽間動不動就爆滿,這時候 我老媽(GC)就出場了,她總是默默地趁我姐沒在試衣的時候(Stop-the-world)把她不經常穿的衣服都整理出來捐了,把她經常穿的衣服掛在她房間(老年代)的晾衣架上方便她用,這樣我老姐下次回來衣帽間又空出來一大塊,又能肆無忌憚地試衣服了。
同理,程序在運行的過程中會通過NEW等方法創建出一系列的對象,當這些對象被棄用之後就會佔據一定的內存空間,此時就需要GC去清理這些無用的對象來保證後續程序運行時有足夠的內存空間,避免發生內存泄漏OOM(OutOfMempry),GC主要在堆上進行操作,又稱堆GC。
判斷垃圾(找到不常穿的衣服)
A 引用計數算法
我姐會給她的每套衣服都拍上一張照片,如果她覺得衣服好看,就會多拍幾張,如果她覺着不好看了,就會回頭把那些衣服的照片都刪掉,於是我老媽特地從她的照片上去判斷哪些衣服出現過,如果有些衣服一張照片都沒有了,就說明這衣服她不喜歡了,那就把這衣服捐了,衣帽間就又空了。
同理,引用計數算法通過判斷每個對象自帶的引用計數器去決定其是否被回收,如被引用一次則+1,完成一次則-1,當引用次數爲0時此對象便會被當做垃圾收集。
√ 優點:效率高,只需要遍歷計數器。
× 缺點:程序存在循環引用的情況下無法被篩選,造成內存泄漏。(即我姐的照片裏面出現了一些衣服,但是並沒有穿在身上,我媽以爲她還喜歡,但是其實這些衣服是不小心出鏡的。)
B 可達性算法
爲了避免引用計數算法缺點造成的尷尬,於是需要改進一下判斷算法,必須是我姐穿在身上出鏡的衣服,纔是她要穿的衣服。
可達性算法即通過GC Root向下查詢引用鏈,一步步找到可以到達的對象,而不可達的對象則被視爲要回收的垃圾,那麼GC Root又是啥東東
GC Root的具體體現:
- java虛擬機棧中的引用變量
- 方法區中的類靜態屬性引用的對象
- 方法區中的常量引用的對象
- 本地方法棧中JNI本地方法的引用對象
- 活躍線程的引用對象(萬物皆對象)
常見的垃圾回收算法(4種)
標記清除算法
標記清除算法是指在找到垃圾之後直接在內存空間上將其清理,但是由於太過簡單,會造成很多碎片化的空間。
標記整理算法
標記整理算法則在標記清除算法的基礎上對清理前的內存空間進行了整理,使得被佔用的內存有效在排放在緊湊的區域,從而使得許多碎片化的空閒內存可以連續,從而方便一些佔用內存較大的對象保存。
複製算法
複製算法則是乾脆先找出一片大塊連續的空閒內存,先將有效對象從各處複製到空閒區域並排好,隨後將其他區域一併清理。爲了避免所有對象都是可用對象的情況,所以複製算法要求必須存在一片空閒區域佔50%以上的空間,這樣就顯得有些浪費。由於此算法需要用到複製等操作,所以適用於失效比較頻繁的堆區域,即年輕代。
分代收集算法
分代收集算法顧名思義則即分年代收集,分爲年輕代和老年代的收集(此前還有永久代)。
年輕代 Minor GC (Young GC)
年輕代的內存區域分爲三部分,它們分別是Eden區(伊甸區)和兩個Survivor區(倖存者區),新創建的對象基本會在Eden區,當每次發生MinorGC 的時候,GC就會把被引用的對象從Eden區採用複製算法放置在某個Survivor區內,此時這個Survivor區就被稱爲To區,當下次GC的時候,從Eden區新過來的對象就和上一次放在To區的對象一起復制到另外一個Survivor區內,而此時之前的Survivor區就被叫做From區,接受對象的Survivor區則變成To區,每次複製會給存活的對象計數+1,當計數達到設定的閾值時,則會將此對象移入老年代。
☆ MinorGC耗費時間較短,發生的頻率也較高。
老年代 Full GC (Major GC)
其實Full GC其實不單單是對老年代進行垃圾收集,它也是包括對新生代的垃圾收集,所以其耗費的時間相當長,所以執行的頻率也相對比較低。
對於Full GC我們更多地需要了解的是它的觸發條件:
- 老年代的內存空間不足的情況下
- 老年代根據每次新生代轉移過來的內存平均值判斷內存不夠下一次轉移的情況下
- 程序調用System.gc()的時候(這只是建議)。
- 計時器觸發
Stop-the-world
不管是何種垃圾回收算法,其在運行的時候,所有線程都會停止爲此讓路,從而避免邊收拾邊產生垃圾的情況,這樣一來,“世界停止”的時間就顯得尤爲重要了,一旦造成太長則會容易影響其他程序的運行,對於一些嚴格控制時間的程序(如廣告播放)是極爲不利的。
從分代收集算法的兩個年代的特點我們也不難得出GC影響正常程序的四種情況:
☆☆ Minor GC的單次執行時間過長/Full GC的執行次數態頻繁
☆ Full GC的單次執行時間過長/Minor GC的執行次數態頻繁
常見的垃圾收集器(7種)
年輕代垃圾收集器
- Serial收集器(複製算法):單線程的收集器,簡單高效
- ParNew收集器(複製算法):多線程收集器,多部分代碼與Serial重合
- Parallel Scavenge(複製算法):同上,但是更注重系統吞吐量
老年代垃圾收集器
-
Serial Old 收集器(標記整理算法):單線程的老年版本,可配合上述所有年輕代收集器。
-
Parallel Old 收集器(標記整理算法):多線程老年版本,配合上述第3種,注重吞吐量。
-
Concurrent Mark Sweep收集器(標記清除算法):併發處理,注重暫停時間,可配合上述第1和第2種收集器。
-
Garbage First收集器(複製+標記整理算法):無年輕代與老年代之分。
面試常考之finalize()方法
當gc準備釋放某個對象的內存地址空間時,會給與其兩次機會,首先會將其置於F-Queue
隊列,判斷其是否使用過finalize()方法,如果未使用過則運行finalize()方法給其復活的機會,如果使用過則直接回收
面試常考之四種引用
- 強引用:寧可拋出OutOfMemoryError也不會清除
- 軟引用:內存空間不足時會被GC清除
- 弱引用:每次GC都會清除只具有弱引用的對象,在GC前可以通過GET獲取對象
- 虛引用:任何時候都會被GC回收,用作判斷GC何時啓動,無法通過GET獲取對象