一、概述
1、垃圾回收(GC,Garbage Collection):1960年誕生于MIT的Lisp。當需要排查各種記憶體溢出、記憶體洩漏的時候,當垃圾收集稱為系統達到更高并發品質的瓶頸時,我們需要對這些“自動化”技術實施必要的監控和調節。GC主要用于完成三件事情:
a、那些記憶體需要回收
b、什麼時候回收
c、如何回收
2、什麼時候回收對象
在堆裡存放着java中幾乎所有的對象執行個體。垃圾收集器在對堆進行回收前。第一件事情就是要确定這些對象那些還活着,那些已經死去
1)引用計數算法:給對象添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就建減一。
缺點:難以解決對象之間互相引用的問題。
2)可達性分析算法:通過一系列的稱為“Gc Roots”的對象為起始點,從這些節點開始向下搜尋,搜尋走過的路徑被稱為引用鍊(Reference Chain),當一個對象到GC Roots沒用任何引用鍊相連時,證明次對象是不可以用的。但是以會被判定為可回收的對象

eg、5,6,7可回收
GC Roots:虛拟機棧中的引用對象。方法區中的靜态屬性引用的對象、方法區中靜态屬性引用的對象、本地方法棧中JNI引用的對象
3)垃圾回收中的引用
a、我們前邊的兩個算法中中所提到的判斷方法,都與“引用”有關。在JDK1.2之後,将引用分為強引用、軟引用、弱引用、虛引用。這4種引用的強度依次依次減弱
強引用:在程式代碼中普遍存在的,類似“Object obj = new Object()”這類的引用,隻要強引用還在,垃圾收集器永遠不會回收掉被引用的對象。
軟引用:用來描述一些有用但是非必需的對象。對于軟引用關聯着的對象,在系統将要發生記憶體溢出異常之前,将會将這些對象列進回收範圍之中進行第二次回收。。SoftReference類來實作引用。
弱引用:描述非必需對象,比軟引用更弱,被弱引用關聯的對象隻能生存道下一次垃圾收集發生之前,當垃圾收集器工作室,無論目前記憶體是否足夠,都會回收掉之被弱引用關聯的對象。WeskReference
虛引用:是最弱的一種引用,一個對象是否有虛引用,完全不會對生命周期構成影響。也無法通過虛引用來取得一個對象執行個體。為一個對象設定虛引用關聯的唯一目的就是在這個對象呗垃圾收集器回收時,收到一個系統通知。PhanReference
4)垃圾回收緩行執行
即使對象不可達,也不一定要被回收。如果對象沒有與GC Roots互相連接配接的引用鍊,那麼他就會被第一次标記,且進行一次篩選。
二、垃圾收集算法
1、标記-清除算法
1)首先标記出所有需要回收的對象,在标記完成之後統一回收所有被标記的對象。
2)缺點:
a、效率問題:标記和清除兩個過程的效率不算高
b、空間問題:标記清除之後會産生大量不連續的記憶體碎片
2、複制算法
1)為了解決效率問題,複制算發可以将記憶體按照容量的大小劃分為大小相等的兩塊,每次隻使用其中的一塊。當這一塊記憶體用完了,就将還存活着的對象複制到另外一塊上,然後把已經使用過的記憶體空間一次性清理。
2)優點:實作簡單,運作高效
3)缺點:代價高昂
3、标記-整理算法
1)複制算法在對象存活率較高的時候要進行較多的複制操作,效率将會變得更低。更關鍵的是,如果不想浪費50%的空間,就需要有額外的空間進行配置設定擔保。以應對被使用的記憶體中的對象都100%存活的極端情況。是以老年代一般不能直接選用這種算法。
标記-整理算法中,标記的過程與“标記-清除”中的标記算法一樣,但是後續步驟不是直接清理,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的記憶體。
4、分代收集算法
1)更具對象存活周期的不同将記憶體劃分為幾塊,一般是吧java堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最适當的收集算法。新生代适合複制,老年代适合“标記-清除”和“标記-整理”。
三、HotSpot的算法實作
上邊我們介紹了對象存活判定算法和垃圾收集算法,而在HotSpot虛拟機中,要實作這些算法,必須對算法的執行效率有嚴格的考量,才能保證虛拟機的高效運作。
1)枚舉根節點
從可達性分析中從GC Roots節點找引用鍊這個操作為例子,可作為GC Rootts主要的全局性的引用(例如常量或靜态屬性)與執行上下文中
2)安全點 3)安全區域
四、垃圾收集器
收集算法是記憶體回收的方法論,垃圾收集器就是記憶體回收的具體實作。JAVA虛拟機規範中對垃圾收集器應該如何實作沒有具體的規定,是以不同的廠商、不同的版本有很大的差別。
1、圖中展示了七種作用于不同分代的收集器,如果兩個收集器之間存在連線,就說明它們可以搭配使用,虛拟機所處的區域,則表明它是屬于新生代收集器還是老年代收集器。
2、Serial收集器
Serial收集器是最基本的、發展曆史最悠久的收集器,這個收集器是一個單線程收集器,它的“單線程”說明的是,他進行垃圾收集時,必須暫停其他所有工作線程,直到它收集結束
優點:他依然是虛拟機運作在Client模式下的預設新生代收集器,簡單而高效(與其他收集器的單線程比),對于限定單個CPU的環境來說,Serial收集器由于沒有線程互動的開銷,專心做垃圾收集自然有着獲得最高的單線程收集效率。
2、ParNew收集器
ParNew收集器其實就是Serical收集器的多線程版本,除了使用多線程進行垃圾收集之外,其餘的行為包括Serial收集器可用的所有控制參數、收集算法、STop the World、對象配置設定規則、回收政策都與Sercial收集器完全一樣。
3.G1收集器
G1是一款面向服務端應用垃圾收集器。HotSpot開發團隊賦予他的使命是(在比較長期的)未來可以替換掉JDK1.5中釋出的CMS收集器。與其他GC收集器相比G1具備如下特點。
并行與并發:G1能充分的利用多CPU、多核環境下的硬體優勢,使用多個CPU(CPU或者CPU核心)來縮短Stop-The-World停頓的時間,部分其他收集器原本需要停頓java線程執行的Gc動作,G1收集器任然可以通過并發的方式讓java程式繼續執行。
分代收集:與其他收集器一樣,分代的概念在G1中依然得以保留,雖然G1及惡意不需要其他收集器配合就能獨立管理整個GC堆,但它能夠有采用不同的方式去處理行建立的對象和以及存活率一段時間、熬過多次GC的就對象以擷取更好的手機效果。
空間整合:與CMS的“标記--清理”算法不同,G1從整體上來看是基于“标記--整理”算法實作的資料收集。
可預測的停頓:能讓使用者明确制定在一個長度為M浩渺的時間片段内,消耗在垃圾收集上的時間不得超過N毫秒,這就已經是實施java的垃圾收集器的特征量。
G1收集器時,java堆的記憶體布局就與其他的收集器有很大的差別,他将整個java堆劃分為多個大小相等的獨立區域。雖然還保留着新生代和老年代的概念,但是新生代和老年代不再是實體隔離,他們是一部扽Region的集合。
1)Region之間對象應用導緻的堆掃描解決方式
在G1收集器中,Region之間的對象引用以及其他收集器的中的新生代與老年代之間的對象應用,虛拟就都是使用了Remembered Set來避免全堆掃面的。G1中每個Region都有一個與之對應的Remembered Set,虛拟機發現程式對Reference類型的資料進行寫操作室,會産生一個Write Barrier暫時中斷寫操作,檢查Reference應用的對象是否處于不同的Region之間,如果是,便通過CardTable吧相關引用資訊記錄到被應用對象所屬的Region的Remembered Set中。
2)G1:收集器的運作
a:初始标記(Initia Marking)
b:并發标記(COncurrent Marking)
c:最終标記(Final Marking)
d:篩選回收
四、GC日志
1)最前面的“33.125”和“100.667”:GC發生的時間,從虛拟機啟動以來經過的秒數
2)GC日志開頭的“[GC”和"[Full GC"說明了這次GC是發生了Stop-The-World的
3)接下來的“[DefNew”、“[Tenured”、“[Perm”表示GC發生的區域,這裡的區域名稱與使用的GC收集器是相關的,如果是Serial收集器為“[DefNew”,如果是ParNew收集器,新生代名稱就回變為“[ParNew”。
4)後邊的括号内部的“3324K->152K(3712k)”含義是:GC前記憶體區域已使用容量->GC後記憶體區域已使用容量(該記憶體區域總容量)。
5)方括号之外的"3324K->152K(11904K)"含義是:GC前java堆已使用容量->GC後Java堆已使用容量(Java堆總容量)
6)再往後“0.0025925 secs”表示該記憶體區域GC所占的時間,有的會給出更詳細的時間,