引用類型
-
強引用:發生 gc 的時候不會被回收java
-
軟引用:有用但不是必須的對象,在發生內存溢出以前會被回收算法
-
弱引用:有用但不是必須的對象,在下一次 GC 時會被回收服務器
-
虛引用(幽靈引用/幻影引用):沒法經過虛引用得到對象markdown
用 PhantomReference 實現虛引用,虛引用的用途是在 gc 時返回一個通知多線程
垃圾辨別方法
- 引用計數器
- 爲每一個對象建立一個引用計數,有對象引用時計數器 +1,引用被釋放時計數 -1
- 當計數器爲 0 時就能夠被回收。缺點是不能解決循環引用的問題
- 可達性分析
- 從 GC Roots 開始向下搜索,搜索所走過的路徑稱爲引用鏈
- 當一個對象到 GC Roots 沒有任何引用鏈相連時,則證實此對象是能夠被回收的
GC Roots,GC 的根集合, 是一組必須活躍的引用併發
可做爲 GC Roots 的對象有:性能
- 虛擬機棧(棧幀中的本地變量表)中引用的對象
- 方法區中類靜態屬性引用的對象
- 方法區中常量引用的對象
- 本地方法棧中 JNI(即通常說的 native 方法)中引用的對象
垃圾收集算法
-
引用計數(Reference Counting)spa
- 原理是此對象有一個引用,即增長一個計數,刪除一個引用則減小一個計數
- 垃圾回收時,只用收集計數爲 0 的對象
- 缺點:沒法處理循環引用問題
-
標記-清除(Mark-Sweep)線程
- 第一階段從引用根節點開始標記全部被引用的對象
- 第二階段遍歷整個堆,把未標記的對象清除
- 缺點:此算法須要暫停整個應用,同時,會產生內存碎片
-
複製(Copying)指針
- 把內存空間劃爲兩個相等的區域,每次只使用其中一個區域
- 垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另一個區域中。每次只處理正在使用中的對象
- 由於複製成本比較小,同時複製過去之後還能進行相應的內存整理,不會出現「碎片」問題
- 缺點:須要兩倍內存空間
-
標記-整理(Mark-Compact)
- 第一階段從引用根節點開始標記全部被引用對象
- 第二階段遍歷整個堆,將全部存活的對象都向一端移動,而後直接清除掉端邊界之外的內存
- 此算法避免了「標記-清除」的碎片問題,同時也避免了「複製」算法的空間問題
-
分代(Generational Collecting)
- 基於對對象生命週期分析後得出的垃圾回收算法
- 把堆中對象分爲年青代、年老代、持久代(JDK8 不存在持久代)
- 按照對象經歷的 GC 次數來肯定是哪一代
- 對不一樣代使用不一樣的算法進行回收
- 如今的垃圾回收器通常使用此算法,是多種基本回收算法的組合使用
分代回收算法
起源:研究發現,大部分 java 對象只存活一小段時間,而存活下來的小部分 java 對象則會存活很長一段時間
簡單來講,將堆分紅兩部分,年輕代用來存放新對象,當對象存活時間夠長時,移動到年老代
堆的分代
-
年輕代 Young Generation
- 默認佔總空間的 1/3(經過 -XX:NewRatio 指定年輕代和老年代比例)
- 分爲 Eden、To Survivor、From Survivor 三個區,默認佔比 8:1:1(經過 -XX:SurvivorRatio 指定)
-
年老代 Tenured Generation
-
持久代 Perm Generation(JDK8後不存在)
- 即方法區,用於存放靜態文件,現在Java類、方法等
- 持久代對垃圾回收沒有顯著影響
- 在 JDK8 中,廢棄了持久代,改用元空間(metaspace)實現方法區,屬於本地內存
分代收集
-
年輕代回收器
-
假設大部分對象都存活很短期,須要頻繁採用耗時較短的垃圾回收算法
-
新生代垃圾收集器通常採用複製算法,優勢是效率高,缺點是內存利用率低
-
垃圾收集器有:Serial、ParNew、Parallel Scavenge
-
年老代回收器
- 假設老年代中的對象大機率繼續存活,真正觸發老年代 gc 時,表明假設出錯或堆空間已耗盡,通常須要全堆掃描,全局垃圾回收
- 老年代收集器通常採用的是標記-整理的算法進行垃圾回收
- 垃圾收集器有:Serial Old、Parallel Old、CMS
-
整堆回收器
G1:兼顧吞吐量和停頓時間的 GC 實現,JDK 9 之後的默認 GC 選項
回收過程
新對象存放在年輕代的 Eden 分區,Eden 空間耗盡時,觸發 gc,通常使用複製算法
年老代空間佔用到達某個值以後就會觸發全局垃圾收回,通常使用標記整理算法
- 把 Eden 和 From Survivor 存活的對象放入 To Survivor 區
- 清空 Eden 和 From Survivor 分區
- From 和 To 交換指針,保證下次 gc 前To Survivor 爲空
- Survivor 分區的對象,通過一次複製年齡就 +1,年齡到達 15時(默認 15),Survivor 分區升級爲老生代。對象也會直接進入年老代
gc 類型
-
Minor GC
- 通常狀況下,當新對象生成,而且在 Eden 申請空間失敗時,就會觸發Minor GC
- 在年輕代 Eden 區域進行GC,清除不存活對象,而且把尚且存活的對象移動到 Survivor 區。而後整理 Survivor 的兩個區
- 很頻繁的 gc,不影響老年代
-
Full GC
對整個堆進行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,所以應該儘量減小Full GC。有以下緣由可能致使Full GC:
- Tenured 被寫滿
- Perm 域被寫滿(JDK8 以前)
- System.gc( ) 被顯示調用
- 上一次 GC 以後堆的各域分配策略動態變化
垃圾收集器
收集器分類
-
串行收集器
-
並行收集器
-
對年輕代進行並行垃圾回收,能夠減小垃圾回收時間。通常在多線程多處理器機器上使用
使用 -XX:+UseParallelGC 打開
-
並行收集器 jdk5 引入,在 jdk6 中進行了加強,可對堆年老代進行並行收集
使用 -XX:+UseParallelOldGC 打開
-
若是年老代不使用併發收集,而使用單線程進行垃圾回收,會制約擴展能力
-
併發收集器
- 能夠保證大部分工做都併發進行(應用不中止),垃圾回收只暫停不多的時間
- 此收集器適合對響應時間要求比較高的中、大規模應用
- 使用 -XX:+UseConcMarkSweepGC 打開
常見收集器
- Serial:最先的單線程串行垃圾回收器
- Serial Old:Serial 垃圾回收器的老年版本,一樣也是單線程的,能夠做爲 CMS 垃圾回收器的備選預案
- ParNew:是 Serial 的多線程版本
- Parallel :
- 和 ParNew 收集器相似,是多線程的收集器
- Parallel 是吞吐量優先的收集器,能夠犧牲等待時間換取系統的吞吐量
- Parallel Old:
- 是 Parallel 老生代版本
- Parallel 使用複製算法,Parallel Old 使用標記-整理算法
- CMS:一種以得到最短停頓時間爲目標的收集器,很是適用 B/S 系統
- G1:一種兼顧吞吐量和停頓時間的 GC 實現,是 JDK 9 之後的默認 GC 選項
CMS 收集器
-
CMS:Concurrent Mark-Sweep
-
CMS 使用標記-清除的算法
- 在 gc 時候會產生大量的內存碎片
- 當剩餘內存不能知足程序運行要求時,系統將會出現 Concurrent Mode Failure
- 臨時 CMS 會採用 Serial Old 回收器進行垃圾清除,此時的性能將會被下降