Java垃圾收集器與內存分配策略

 一、對象存活斷定算法 :算法

1)引用記數算法:給每一個對象添加一個引用計數器,每當有一個地方引用,計數器就加1,當引用失效時就減1,任什麼時候刻計數器爲0的對象就是不可能再被引用的。可是主流的Java虛擬機裏面沒有選用引用計數器算法來管理內存,其中最主要的緣由是它很難解決對象之間相互循環引用的問題。數組

2)可達性分析算法:經過一系列稱爲「GC Roots」的對象做爲起始點,從這些節點向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GCRoots沒有任何引用鏈時,則證實此對象是不可用的。主流實現。安全

在Java語言中,可做爲GCRoots的對象包括下面幾種:a.虛擬機棧中引用的對象;b.方法區中類靜態屬性引用的對象;c.方法區中常量引用的對象;d.本地方法棧中JNI引用的對象。數據結構

二、生存仍是死亡多線程

真正宣告一個對象死亡,至少要經歷兩次標記過程:併發

1)可達性分析後發現不可達,進行第一次標記,並將沒有必要執行finalize方法的對象放到一個F-Queue隊列;線程

2)GC對F-Queue隊列中的對象進行第二次小規模標記,若是仍是不可達,基本就真的被回收了。日誌

三、關於finalize方法對象

1)當對象沒有覆蓋finalize方法,或者finalize方法已經被虛擬機調用過,虛擬機將這兩種狀況都視爲「沒有必要執行」,所謂「執行」是指虛擬機會觸發發這個方法,但並不承諾會等待它運行結束;隊列

2)finalize方法只會被系統調用一次;

3)finalize方法能作的全部工做,try-finally或者其它方式均可以作的更好、更及時,因此建議徹底能夠忘掉Java語言中這個方法的存在。

四、垃圾收集算法:標記-清理、複製、標記-整理、分代收集

1)標記-清理算法

a.分兩個階段:標記和清理,首先標記出須要回收的對象,在標記完成後統一回收全部被標記的對象;

b.不足:一個是效率問題,標記和清理兩個過程的效率不高;另外一個是空間問題,標記清理後會產生大量不連續的內存碎片,空間碎片太多可能會致使之後在程序運行過程當中須要分配較大對象時,沒法找到足夠的連續內存而不得不提早觸發一次垃圾收集動做。

2)複製算法

a.將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當一塊的內存用完了,就將還存活的對象複製到另外一塊上面,而後再把已使用的內存空間一次清理掉。效率提升,也沒有內存碎片的問題。代價是內存縮小爲原來的一半。

b.新生代的對象98%是「朝生夕死」的,因此並不須要按照1:1的比例來劃份內存空間,而是將內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor。當回收時,將Eden和Survivor中還存活的對象一次性複製到另一塊Survivor空間上,最後清理掉Eden和剛纔用過的Survivor空間。HotSpot虛擬機默認Eden和Survivor的大小比例是8:1,也就是每次新生代中可用內存空間爲整個新生代容量的90%,只有10%的內存會被「浪費」。若是另一塊Survivor空間沒有足夠的空間存放上一次新生代收集下來的存活對象時,這些對象將直接經過分配擔保機制進入老年代。

3)標記-整理算法

a.分兩個階段:標記和整理,首先標記出須要回收的對象,而後讓存活的對象都往一端移動,而後直接清理掉邊界之外的內存。

4)分代收集算法

a.通常把Java堆分紅新生代和老年代,這樣就能夠根據各個年代的特色採用最適當的收集算法。在新生代,每次垃圾收集時都發現有大批對象死去,只有少許存活,那就選用複製算法,只須要付出少許存活對象的複製成本就能夠完成收集。而老年代中由於對象存活率高、沒有額外空間對它進行分配擔保,就必須使用「標記-清理」或者「標記-整理」算法來進行回收。

五、HotSpot的算法實現

1)枚舉根結點:因爲目前的主流Java虛擬機使用的都是準確式GC,因此當執行系統停頓下來後,並不須要一個不漏的檢查完全部執行上下文和全局的引用位置,虛擬機應當有辦法直接得知哪些地方存放着對象引用。在HotSpot的實現中,使用一組成爲OopMap的數據結構來記錄對象引用。

2)安全點:程序在執行時並不是全部的地方都能停頓下來開始GC,只有在到達安全點時才能暫停。安全點的選定既不能太少以至於讓GC等待時間太常,也不能太頻繁以至於過度增大運行時負荷。因此安全點的選定基本上是以程序「是否具備讓程序長時間執行的特徵」爲標準進行選定的。「長時間執行」最明顯的特徵就是指令序列複用,例如方法調用、循環跳轉、異常跳轉等,因此具備這些功能的指令纔會產生安全點。另一個須要考慮的問題時如何在GC發生時讓全部線程「跑」到最近的安全點上再停頓下來。這裏有兩個方案:搶佔式中斷和主動式中斷。如今幾乎沒有虛擬機實現採用搶佔式中斷來暫停線程從而相應GC事件。而主動式中斷的思想時當GC須要中斷線程的時候,不直接對線程操做,僅僅簡單地設置一個標誌,各個線程執行時主動去輪詢這個標誌,發現中斷標誌爲真時就中斷掛起,輪詢標誌的地方和安全點是重合的,另外再加上建立對象須要分配內存的地方。

三、安全區域:指在一段代碼片斷之中,引用關係不會發生變化。在這個區域中的任意地方開始GC都是安全的。在線程執行到安全區域中的代碼時,首先標識本身已經進入安全區域,那樣,當在這段時間裏JVM要發起GC時,就不用管標識本身爲安全區域狀態的線程了。在線程要離開安全區域時,它要檢查系統是否已經完成根結點枚舉,若是完成了,那線程就繼續執行,不然它就必須等待直到收到能夠安全離開安全區域的信號爲止。 

六、垃圾收集器:a.新生代收集器:Serial、Parnew、Parallel Scavenge,b.老年代收集器:Parallel Old、CMS、Serial Old,c.G1。

1)Serial收集器:單線程收集器,在它進行垃圾回收時,必須暫停其餘全部的工做線程,直到它收集結束;

2)ParNew收集器:Serial收集器的多線程版本,除Serial收集器外,目前只有它能與CMS收集器配合工做;

3)Parallel Scavenge收集器:目標時達到一個可控制的吞吐量,也稱「吞吐量優先」收集器;

4)Serial Old收集器:Serial收集器的老年代版本,一樣是一個單線程收集器,使用「標記-整理」算法;

5)Parallel Old收集器:Parallel Scavenge收集器的老年代版本,使用多線程和「標記-整理」算法,在注重吞吐量以及CPU資源敏感的場合,均可以優先考慮Parallel Scavenge + Parallel Old收集器。

6)CMS收集器:以獲取最短回收停頓時間爲目標的收集器,基於「標記-清理」算法實現的。優勢是:併發收集、低停頓。

7)G1收集器:它將整個Java堆劃分爲多個大小相等的獨立區域,可以創建可預測的停頓時間模型。G1跟蹤各個Region裏面的垃圾堆積的價值大小,在後臺維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的Region。

七、GC日誌:每種收集器的日至不同,但都有共性,如下是典型的GC日誌

1)33.125: [GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs]

2)100.667: [Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->210K(19456K), [Perm: 2999K->2999K(21248K)], 0.015007 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]

a.最開始的數字表明GC發生的時間,這個數字的含義是從Java虛擬機啓動以來通過的秒數;

b.[GC 和[Full GC說明了此次垃圾收集的停頓類型,而不是來區分新生代GC和老年代GC的。Full,說明此次GC發生了一次Stop-The-World。

c.[DefNew 、[Tenured、[Perm表示GC發生的區域,不一樣的垃圾收集器顯示的名稱不一樣

d.後面方括號內部3324->152K(3712)表示「GC前該內存區域已使用容量->GC後該內存區域已使用容量(該內存區域總容量)」

e.方括號以外的3324->152K(11904K)表示「GC前Java堆已使用容量->GC後Java堆已使用容量(Java堆總容量)」

f.再日後,0.0025925 secs表示該內存區域GC所佔用的時間,單位是秒,有的收集器會給出更具體的時間,user、sys、real分別表示用戶態消耗的CPU時間、內核態消耗的CPU時間和操做從開始到結束所通過的牆鍾時間(Wall Clock Time)。CPU時間與牆鍾時間的區別是,牆鍾時間包括各類非運算的等待耗時。

八、內存分配規則:如下是幾條最廣泛的內存分配規則

1)對象優先在Eden分配;

2)大對象直接進入老年代:所謂大對象是指須要大量連續內存空間Java對象,最典型的大對象就是那種很長的字符串以及數組,避免短命大對象;

3)長期存活的對象直接進入老年代:虛擬機給每一個對象定義了一個對象年齡計數器,每次MinorGC後仍然存活,而且能被Survivor容納的話,對象年齡加1,當它的年齡到達必定程度(默認15歲),就會晉升到老年代。

4)動態對象年齡斷定:虛擬機不是必需要求對象的年齡必須達到MaxTenuringThreshold才能晉升老年代,若是Survivor空間中相同年齡全部對象大小總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就能夠直接進入老年代。

九、空間分配擔保

在發生MinorGC以前,虛擬機會先檢查老年代最大可用的連續空間是否大於新生代全部對象總空間,若是這個條件成立,那麼MinorGC能夠確保是安全的,若是不成立,則虛擬機會查看HandlePromotionFailure設置值是否容許擔保失敗,若是容許,那麼會繼續檢查老年代最大可用的連續空間是否大於歷次晉升到老年代對象的平均大小,若是大於,將嘗試進行一次MinorGC,儘管此次MinorGC是有風險的,若是小於或者HandlePromotionFailure設置不容許冒險,那這時也要改成進行一次FullGC。

相關文章
相關標籤/搜索