堆中幾乎存放着全部的對象實例,垃圾收集器在對堆回收以前,第一件事情就是要肯定這些對象哪些還「存活」,那些對象已經「死去」(即不可能被任何途徑使用的對象)算法
給對象中添加一個引用計數器,每當又一個地方引用它時,計數器值加1;當引用失效時,計數器減1;任什麼時候刻計數器都爲0的對象就是不可能在被使用的.可是Java語言中沒有選用引用計數法來管理內存,其中最主要的緣由是它很難解決對象之間相互循環依賴的問題.bash
例如: 在testGC中,對象objA和對象objB都有字段instance,賦值objA.instance=objB.instance及objB.instance=objA.instance,除此以外這兩個對象再無其餘任何引用,實際上這兩個對象都已經不能再被訪問,可是他們由於他們互相引用着對方,他們的引用計數都不爲0,因而引用計數算法沒法通知GC收集器回收它們. 打印GC詳細信息: -XX:+PrintGCDetailsspa
public class ReferenceCountingGC {
public Object instance = null;
public static void main(String[] args) {
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
//假設在這行發生了gc,objA和objB是否被回收
System.gc();
}
}
複製代碼
GC日誌爲:3d
[GC (System.gc()) [PSYoungGen: 334480K->4736K(334848K)] 597914K->270331K(1017536K), 0.0019879 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [PSYoungGen: 2408K->0K(298688K)] [ParOldGen: 0K->3363K(682688K)] 3408K->3363K(981376K), [Metaspace: 3162K->3162K(1056768K)], 0.0050515 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 153088K, used 3947K [0x0000000715580000, 0x0000000720000000, 0x00000007c0000000)
eden space 131584K, 3% used [0x0000000715580000,0x000000071595afc0,0x000000071d600000)
from space 21504K, 0% used [0x000000071d600000,0x000000071d600000,0x000000071eb00000)
to space 21504K, 0% used [0x000000071eb00000,0x000000071eb00000,0x0000000720000000)
ParOldGen total 349696K, used 872K [0x00000005c0000000, 0x00000005d5580000, 0x0000000715580000)
object space 349696K, 0% used [0x00000005c0000000,0x00000005c00da3b8,0x00000005d5580000)
Metaspace used 3169K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 346K, capacity 388K, committed 512K, reserved 1048576K
複製代碼
對照上面的圖,GC日誌中的PSYoungGen(PS是指Parallel Scavenge)爲Eden+FromSpace,而整個YoungGeneration爲Eden+FromSpace+ToSpace。日誌
咱們設置的新生代大小爲10240K,這包括9216K大小的PSYoungGen和1024K大小的ToSpace。其中,PSYoungGen中的Eden:FromSpace爲8:1,code
這包括8192K的Eden和1024K的FromSpace。cdn
參數設置爲:對象
-XX:+PrintGCDetails -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=8 -XX:NewSize=10M -XX:MaxNewSize=10Mblog
參數解釋:內存
-XX:+PrintGCDetails 啓用日誌
-XX:-UseAdaptiveSizePolicy 禁用動態調整,使SurvivorRatio能夠起做用
-XX:SurvivorRatio=8 設置Eden:Survivior=8
-XX:NewSize=10M -XX:MaxNewSize=10M 設置整個新生代的大小爲10M
默認垃圾收集器爲:Parallel Scavenge
在運行日誌結果中咱們能夠看到GC日誌包含「2408K->0k」,老年代從2408K(約2M,其實就是objA與objB)變爲了512K,意味着虛擬機並無由於這兩個對象互相引用就不回收它們,這也證實虛擬機並非經過引用計數法判斷對象是否存活的.能夠看到對象進入了老年代,可是你們知道,對象剛建立的時候是分配到新生代中到,要進入老年代須要到new ObjA才行,可是這裏objA和objB卻進入了老年代.這是由於Java堆區會動態增加,剛開始時堆區較小,對象進入老年代還有一些規則,當survior空間中同一代的對象大小之和超過survior空間的一半時,對象將直接進入老年代.
這個算法的基本思路是經過一系列名爲「GC Root」的對象做爲起點,從這些節點開始往下搜索,搜索走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用到,下圖對象object5,object6,object7雖然有互相判斷,但它們到GC Roots是不可達到,因此它們將會斷定爲可回收對象
在Java語言裏,可做爲GC Roots對象的包括以下幾種:
即便在可達分析算法中不可達的對象,也並不是「非死不可」的,這時候它們暫時處於「緩刑」階段,要真正宣告一個對象死亡,至少要經歷兩次標記過程. 標記的前提是對象在進行可達性分析後發現沒有與GC Roots互相連的引用鏈.