垃圾收集的第一步就是先須要算法來標記哪些是垃圾,而後再對垃圾進行處理。算法
引用計數(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. 緊湊(也稱「整理」):將全部存活對象向內存開始部位移動,稱爲內存緊湊(至關於碎片整理)。完畢後,清理剩餘內存空間。
分代收集策略
因爲不一樣的對象適合使用不一樣的垃圾收集算法,因此引入「代」這個概念。不一樣的代有不一樣的分區,通常分爲新生代區和老年代區。
新生代:適合採用複製算法進行垃圾收集,對象分佈在Eden/S0/S1三個區。
老年代:適合採用標記-緊湊算法進行垃圾收集。
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 分區
下圖:VisualVM 中經過 VisualGC插件顯示的分區
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的永久代區。