引用計數法
對象增加一個引用計數加1,失去減1
判斷對象是可以被回收
1.引用計數為0
2.無法連結到“GC Roots”對象,GC Roots對象有:靜态常量,靜态對象,native對象,虛拟機棧對象(是以兩個對象a,b如果互相引用,然後a=null,b=null,會被判定可以被垃圾回收;不需要stop the word,因為OopMap)
四種引用類型
1.強引用:即一般普遍的引用
2.軟引用:用SorftRefernce實作,在記憶體即将用完時,會被清理,使用場景網頁緩存、圖檔緩存
3.弱引用:弱引用通過WeakReference類實作,在垃圾收集器工作時都會被清理,
4.虛引用: 通過PhantomReference類來實作,無法通過虛引用來獲得執行個體,不能單獨使用,唯一目的是在對象被垃圾回收時可以收到通知

垃圾收集算法
标記清除算法
算法分标記和清除兩個階段。首先标記要清除的對象,在标記完成後同一清除。
方法缺點:1.效率低,2.會産生大量碎片,以後存儲大對象不友善
複制算法:
把記憶體分成大小相等的兩塊,當一塊用完把,把沒有被删除的對象移動到另一塊,餘下的全部删除。一般用于新生代的算法。
方法有點:沒有碎片;缺點:因為分成了兩塊,浪費記憶體資源,對象存活率高時,複制操作較多
标記-整理算法
和标記清除算法類似,增加了一步讓存活對象移動的過程,已減少記憶體碎片。适合老年代。
HotSpot算法實作
枚舉節點
在判斷對象是否存活時,需要尋找GC Roots節點,此時需要保持對象關系不變,是以需要stop the world。為了減少GC Roots消耗的時間導緻的stop the world 時間太長,使用一組OopMap來存儲對象引用資訊,進而快速的完成對象可達性校驗。
安全點
安全點為程式運作時記錄OopMap的點,如方法調用,循環跳轉,異常跳轉。
安全區域
即對象關系不會發生變化的區域,可以看成安全點的擴充。如sleep狀态的線程。
中斷方式
1.搶先式中斷:發生gc時,把所有線程中斷,沒有運作到安全點的程式運作到安全點後在做gc。(一般不用)
2.主動式中斷:為線程在安全點處和需要配置設定記憶體的地方做一個标記,線程執行時主動輪尋這個标志,發現這個标志時主動挂起,執行gc。
記憶體配置設定政策
記憶體分區配置
設定新生代老年代記憶體配置設定:-Xms20M,-Xmx20M,-Xmn10M(java堆記憶體大小20M,其中10M為新生代記憶體)
設定新生代記憶體survivor區(survivor區包括from和to兩個相同大小的區域)的比例:-XX:SurvivorRatio=8(即:eden:from:to = 8:1:1)
對象配置設定流程
新生代:
1.如果啟動了本地線程配置設定緩存,會先配置設定在TLAB上,否則一般先會配置設定到Eden區上。
2.在第一次Minor GC時把eden區存活的對象移動到survivor區的from處
3.在第二次Minor GC時eden區存活的對象被移動到to區,from區存活的對象,如果沒有進入老年,區也被移動到to區
4.重複2,3的過程
老年代:
1.大對象直接進入老年代:如對象很大survivor區記憶體不夠無法放入的情況,這種情況可以設定-XX:PretenureSizeThreshold參數,使大于這個值的對象直接進入老年區,已避免在eden區和survivor區間的記憶體複制。
2.長期存活的對象進入老年代:沒熬過一次minor GC,年齡就增加1,當年齡達到一定程度就晉升到老年代。晉升老年代的年齡通過:-XX:MaxTenuringThreshold設定(預設15)。
3.當survivor區所有對象的大小總和超過其一半時,該區年齡大于等于平均年齡的對象進入老年區。
4.當發生minor GC時,如果HandlePromotionFailure= false老年代的可用連續記憶體放不下新生代的所有對象,将發生時full gc;如果 HandlePromotionFailure= true老年代的可用連續記憶體放不下新生代的所有對象,但是大于曆次晉升到老年代的對象平均大小,就不會full gc。
在這總情況下,老年代可能無法放下本次晉升老年代的對象,導緻HandlePromotionFailure失敗,此時會重新Full GC後再存儲。一般情況下選擇HandlePromotionFailure = true,因為可以避免頻繁的full GC。
垃圾收集器
Serial
隻使用一個cpu或者一條收集線程去完成gc。
新生代用複制算法;老年代用标記-整理算法。
由于沒有線程互動的開銷,适合用戶端模式下的虛拟機。
ParNew
Serial的多線程版本,gc時因為是多線程,速度更快。可以與CMS收集器配合工作,是很多server模式下的首選收集器。
Parallel Scavenge
與ParNew類似,為新生代收集器,但Parallel Scavenge主要追求吞吐量,可以通過 -XX:GCTimeRatio來設定吞吐量的大小。通過-XX:MaxGCPauseMillis來設定停頓時間。 吞吐量 = 運作使用者時間/(運作代碼時間+垃圾收集時間)
Serial Old
Serial老年代版本,與Parallel Scavenge搭配使用,或者做為CMS的後備方案。
Parallel Old
Parallel Scavenge老年代收集器。
CMS(java1.8預設)
詳情見:https://blog.csdn.net/wangyy130/article/details/88758055sCMS GC的終極目标是降低垃圾回收時的暫停時間,是以在該階段要盡最大的努力去處理那些在并發階段被應用線程更新的老年代對象,這樣在暫停的 重新标記階段就可以少處理一些,暫停時間也會相應的降低。
CMS收集器在Minor GC時會暫停所有的應用線程,并以多線程的方式進行垃圾回收。在Full GC時不再暫停應用線程,而是使用若幹個背景線程定期的對老年代空間進行掃描,及時回收其中不再使用的對象。
缺點:
1.因并發設計對cpu敏感,預設啟動回收線程數(cpu數量+3)/4。
2.CMS收集器無法處理浮動垃圾,需要預留白間提供并發收集時的程式操作,通過 -XX:CMSInitiatingOccupancy設定老年區記憶體比,當達到觸發比時觸發gc,如果設定太高導緻“concurrent mode failure”導緻觸發失敗,可以臨時啟用 serial old 來完成老年代的垃圾收集
3.因為 标記-清理 算法,會有記憶體碎片,通過設定 -XX:CMSFullGCsBeforeCompaction,來确定執行多少吃full gc 跟着來一次壓縮。
G1(java1.9預設)
G1将新生代,老年代的實體空間劃分取消了。取而代之的是,G1算法将堆劃分為若幹個區域(Region),其中Humongous區域用來存儲短期巨型對象超過50%分區容積以上。
Gc時把存活對象從一個區域拷貝到另一個區域。