JVM中的垃圾收集算法和Heap分區簡記

如何判斷垃圾對象?

垃圾收集的第一步就是先須要算法來標記哪些是垃圾,而後再對垃圾進行處理。算法

 

引用計數(ReferenceCounting)算法數組

這種方法比較簡單直觀,FlashPlayer/Python使用該算法,簡單高效。核心思路是,給每一個對象添加一個被引用計數器,被引用時+1,引用失效-1,等於0時就表示該對象沒有被引用,能夠被回收。可是,Java/C#並不採用該算法,由於該算法沒有解決對象相互引用的問題,即:當兩個對象相互引用且不被其它對象引用時,各自的引用計數爲1,雖不爲0,但仍然是可被回收的垃圾對象。ide

 

根搜索(GC Roots Tracing)算法優化

基本原理是:GCRoot對象做爲起始點(根)。若是從根到某個對象是可達的,則該對象稱爲「可達對象」(存活對象,不可回收對象)。不然就是不可達對象,能夠被回收。url

 

垃圾收集算法

垃圾收集器一般會假設大部分的對象的存活時間都很是短,只有少數對象的存活時間比較長。spa

垃圾收集算法在JVM中主要是複製算法(新生代GC)和標記/整理算法(老年代GC)。插件

 

標記-清除(Mark-Sweep)算法orm

算法過程:對象

1. 先斷定對象是否可回收,對其標記。
2. 統一回收(簡單地刪除對垃圾對象的內存引用)。內存

優勢:簡單直觀容易實現和理解。缺點:效率不高,內存空間碎片化。

 

複製(Copying)算法

將內存平均分紅A、B兩塊,算法過程:

1. 新生對象被分配到A塊中未使用的內存當中。當A塊的內存用完了, 把A塊的存活對象對象複製到B塊。

2. 清理A塊全部對象。

3. 新生對象被分配的B塊中未使用的內存當中。當B塊的內存用完了, 把B塊的存活對象對象複製到A塊。

4. 清理B塊全部對象。

5. goto 1。

優勢:簡單高效。缺點:內存代價高,有效內存爲佔用內存的一半。

 

對複製算法進一步優化:使用Eden/S0/S1三個分區

平均分紅A/B塊太浪費內存,採用Eden/S0/S1三個區更合理,空間比例爲Eden:S0:S1==8:1:1,有效內存(便可分配新生對象的內存)是總內存的9/10。

算法過程:

1. Eden+S0可分配新生對象;

2. 對Eden+S0進行垃圾收集,存活對象複製到S1。清理Eden+S0。一次新生代GC結束。

3. Eden+S1可分配新生對象;

4. 對Eden+S1進行垃圾收集,存活對象複製到S0。清理Eden+S1。二次新生代GC結束。

5. goto 1。

 

標記-緊湊(Mark-Compact)

算法過程:

1. 標記:標記可回收對象(垃圾對象)和存活對象。

2. 緊湊(也稱「整理」):將全部存活對象向內存開始部位移動,稱爲內存緊湊(至關於碎片整理)。完畢後,清理剩餘內存空間。

spacer.gif

 

分代收集策略

因爲不一樣的對象適合使用不一樣的垃圾收集算法,因此引入「代」這個概念。不一樣的代有不一樣的分區,通常分爲新生代區和老年代區。

新生代:適合採用複製算法進行垃圾收集,對象分佈在Eden/S0/S1三個區。

老年代:適合採用標記-緊湊算法進行垃圾收集。

 

Heap分區和分代概念

Heap分區的目的

1. 爲了分代:不一樣代的對象放到不一樣的內存分區中,實現「代提高」,也方便實現對不一樣分代採用不一樣的垃圾收集算法。

2. 垃圾收集算法須要:新生代GC使用到複製算法,該算法須要將對應的分區劃分紅三個分區:Eden/S0/S1。

 

術語

Generation代

 - YongGeneration/NewGeneration:新生代,在Eden/S0/S1的存活的對象。

 - OldGeneration:老年代,在Tenured區存活的對象。
 - PermanentGeneration:永久代。
Space 區

 - Eden:伊甸園區,是新生代的一個區。
 - Survivor:倖存區,屬於新生代,爲了複製算法的須要。通常分紅大小相等的兩個區(S0/S1或者From/To)。

 - Tenured:存放老年代的區域。
 - Permanent:終身區。

 

下圖:Hotspot 的 Heap 分區

spacer.gif

 

 

下圖:VisualVM 中經過 VisualGC插件顯示的分區

spacer.gif

 

 

Eden/S0/S1 新生代

[Eden                 ][S0     ][S1    ]

S0/S1是大小至關的兩個區域,共同組成Survivor區。

空間比例:Eden:S0==8:1。設定方法:-XX:SurvivorRatio=8。

新生對象在Eden/S0或者Eden/S1中分配,Eden區的對象量達到一個閾值後,發生一次新生代GC。

 

Old 老年代

每一個對象有「對象年齡計數器」。對象由Eden收集到Survivor區後,年齡+1。進行新生代GC後,年齡+1。依次,當年齡>=15後進入老年代。

最大年齡閾值設定:-XX:MaxTenuringThreshold。

動態年齡:若是在Survivor中全部相同年齡對象佔用了空間的一半多,大於等於上述年齡的對象直接進入老年代。

大對象(好比大的數組)直接進入老年代。閾值設定:-XX:PretenureSizeThreshold。

 

Perm 永久代(PermanentGeneration)

用於存放不變對象,如類、方法、字符串等。

Java7把駐留字符串(intentd string)放到了老年代區。Java8中移除了Hotspot的永久代區。

相關文章
相關標籤/搜索