垃圾收集器與內存分配策略

哪些內存需要回收


  • 程序計數器
  • 虛擬機棧
  • 本地方法棧
  • 方法區

描述【程序計數器,虛擬機棧,本地方法棧】這三塊內存區域,都屬於線程私有,會隨着方法入棧,出棧,線程結束,而有條不紊的申請,回收,這三個區域的內存具有確定性,所以會隨着方法或線程的結束而自動的回收,所以不需要考慮內存的回收問題,【堆和方法區】這兩塊內存,由於類的加載和對象的創建都是在程序運行期間才能確定,對象的生命週期也是不盡相同,所以這部分的內存的創建和回收是動態性的,垃圾收集也主要是關注這部分的內存回收

哪些對象可以回收


如何判斷對象已經死亡,不在使用,具備了回收條件

1. 引用計數算法
2. 可達性分析算法【Java選擇這種】

引用計數算法
1.給對象添加一個引用計數器,每當有地方引用,計數器值➕1  引用失效時 引用計數器➖1任何時刻,引用計數爲0的對象,爲不可能再被使用的對象,可被回收
2.缺點 無法處理對象之間循環引用問題

可達性分析算法
通過一系列GC Roots對象作爲起始點,從這些節點向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots 沒有任何引用鏈相連時,則證明這個對象不可用,可被回收

Java可被當做GC Roots的對象有以下幾種
1.虛擬機棧中引用的對象
2.方法區中類靜態變量所引用的屬性
3.方法區中常量引用的對象
4.本地方法棧中JNI引用的對象

Java引用類型的分類


描述 無論是引用計數算法還是可達性分析算法,最終都是跟引用有關係,按照標準的Java對引用的定義,一個對象引用只有引用與沒有被引用兩種狀態,但是有些場景中卻有一些‘食之無味 棄之可惜’的對象,希望虛擬機可以在適當的時機選擇性回收,所以JDK1.2之後對引用概念做了擴充,分爲4大類

強引用
new關鍵字創建的對象就是強引用 這類強引用還存在的對象,垃圾收集器永遠不會回收這被引用的對象

軟引用
JDK1.2之後提供了SoftReferrence類實現軟引用,該類對象的回收時機是在發生內存溢出異常之前,將這些對象列進回收範圍之中進行第二次回收,這次回收還沒有足夠的內存纔會拋出內存溢出異常

弱引用
JDK1.2之後提供了WeakReferrence類實現弱引用,該類對象回收時機是隻能生存到下一次垃圾收集之前,當垃圾收集器工作時,無論內存是否足夠,都會把只被弱引用關聯的對象回收掉

虛引用
JDK1.2之後提供了PhantomReferrence類實現虛引用,回收時機是該引用不會對對象生命週期產生任何影響,唯一的目的就是可以在拉機器回收時收到一個系統通知

垃圾收集算法


標記清除算法

Mark-Sweep 算法
分爲標記和清除兩個階段
1 標記處所有可回收的對象
2 對標記的對象進行回收

缺點:兩階段效率都不高,會產生不連續的內存空間碎片,如果分配大對象時連續的內存空間不足,會再提前出發下一次垃圾收集動作

複製算法

Copying 算法
1.將可用內存劃分爲兩塊,每次只使用其中一塊,其中一塊內存用完了,就會把存活的對象移動到另一塊中,然後將這一塊內存這個回收,快速高效

缺點:內存代價太大,只能使用一半的內存空間

現代垃圾收集器年輕代的垃圾回收都是基於複製算法,但是一般是把內存劃分成Eden和連個Survivor,比例是81而複製使用的是兩個Survivor,
這樣只有10%的內存會被浪費,如果在複製時 Eden和Survivor存活的對象內存大於這個10%內存,會由老年代進行分配擔保,
直接進入老年代

標記整理算法

Mark-Compact 算法
標記階段還是跟標記清除的標記階段一樣,但是在清理階段並不是直接清除,而是先將存活對象移到一端,再清理掉端邊界以外的內存

缺點 會更耗時
優點:內存碎片沒有了

分代收集算法

對前面算法的混合應用,提高收集效率
1 根據對象生命週期的不同,對堆空間進行劃分,分成年輕代【朝生暮死對象】和老年代【生命週期較長對象,大對象】
2 對年輕代使用複製算法 對老年代使用 標記-清除 或者 標記-整理算法

爲了GC時引用關係不發生變化,需要有暫停用戶線程的時機

  • 安全點
  • 安全區域【安全區域是指在一段代碼片段中,引用關係不會發生變化,在這個區域中的任意地方開始GC都是安全的】

垃圾收集器

描述 直到現在都沒有最好的垃圾收集器,更沒有萬能的收集器,只有針對應用更適合的收集器

名詞註釋

  • 並行:指的是多條垃圾收集線程並行工作,但此時用戶線程仍然處於等待狀態
  • 併發:指的是用戶線程與垃圾收集線程同時執行(但不一定是並行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集在另一個CPU上
gantt
dateFormat YYYY-MM-DD
section 年輕代
Serial 、  ParNew  、 Parallel Scavenge: 2014-01-01, 30d
section 整個堆
G1: 2014-01-11, 9d
section 老年代
CMS 、Serial OldParallel Old : 2014-01-01, 30d

年輕代收集器


Serial 收集器
特點
1.單線程收集器
2.垃圾回收時必須暫停其他所有用戶線程,直到垃圾收集結束
3.使用算法 複製算法
4.一般使用在Client模式下,因爲Client模式,一般新生代只有幾十至上百兆,
不會再大了,收集停頓時間在幾十毫秒最多一百多毫秒以內,這個停頓一般是可以接受的
5.最初版本就有

不足:
新生代過大,停頓時間會很長
ParNew 收集器
特點
1.Serial的多線程版本
2.所有的控制參數,收集算法,Stop the World,對象分配規則,回收策略都與serial完全一樣
3.一般運行在Server模式下,也是Server模式下首選的新生代垃圾收集器,因爲除了Serial,只有它可以與CMS 收集器配合使用
4.低停頓
5.使用算法 複製算法
Parallel Scavenge 收集器
特點
1.可控的吞吐量,所謂吞吐量就是CPU運行用戶代碼的時間與CPU總消耗時間的
比值 吞吐量=運行用戶代碼的時間/(運行用戶代碼的時間+垃圾收集的時間)
2.-XX:MaxGCPauseMillis(毫秒)控制垃圾回收的時間不超過這個值 -XX:GCTimeRadio
 默認值 99,既垃圾收集時間佔1/1+99 = 1%;
3.-XX:+UseAdaptiveSizePolicy,GC自適應調節開關,對手工優化存在困難,
可以打開這個開關,只需要設置基本的參數(-Xmx -XX:GCTimeRadio/-XX:MaxGCPauseMillis4.jdk1.4
5.使用算法 複製算法
6.並行的多線收集器
7.適合後臺運算,不需要太多交互的任務,高效利用CPU時間,儘快完成程序計算任務

老年代收集器


Serial Old
1.使用算法 標記-整理算法
2.Serial收集器的老年代版本,一般運行於Client模式下
3.單線程收集器
4.在Server下用途:1.在jdk1.5及之前的版本中與parallel scavenge搭配使用 2.作爲CMS在 Concurrent Mode Failure時的後備預案
Parallel Old
1.使用算法 標記-整理算法
2.jdk1.6
3.Parallel Scavenge 的老年代版本
4.這個收集器的出現纔算有真正意義上的 吞吐量優先應用組合,在CPU資源敏感的場合
可以考慮Parallel OldParallel Scavenge組合
CMS
1.使用算法 標記-清除
2.併發收集 低停頓

缺點
1.對CPU資源非常敏感
2.無法處理浮動垃圾
3.基於標記-清除算法,會存在內存碎片,有兩個參數可以處理這個問題,但是會帶來收集時間的延長
(1).-XX:UseCMSCompactAtFullCollection[默認開啓],在進行FullGC開啓內存碎片的合併整理過程
(2).-XX:CMSFullGCsBeforeComoaction[默認值0],表示執行多少次不壓縮的FullGC後跟着來一次帶壓縮的
4.jdk1.5

管理整個堆的收集器

G1
特點
1.並行與併發
2.分代回收
3.空間整合
4.可預測的停頓
5.整體是基於標記-整理算法
6.Region之間基於複製算法

垃圾收集器參數總結【jdk1.7】

參數 描述
UseSerialGC 虛擬機運行在Client模式下的默認值,打開此開關後,使用Serial+Serial Old 組合進行內存回收
UseParNewGC 打開此開關後使用ParNew+Serial old組合進行內存的垃圾回收
UseConcMarkSweepGC 打開此開關後使用 ParNew+CMS+Serial Old【CMS的後備預案】 組合進行內存的垃圾回收
UseParallelGC 運行在Server模式下的默認值,打開此開關後使用Parallel Scavenge+Serial Old組合
UseParallelOldGC 打開此開關後使用Parallel Scavenge + Parallel Old組合
SurvivorRadio 代表新生代中Eden區域與Survivor 區域的容量比值,默認爲8,代表Eden:Survivor=8:1
PretenureSizeThreshold 直接晉升到老年代的對象大小,設置這個參數後,大於這個大小的對象將直接在老年代分配
MaxTenuringThrehold 晉升到老年代的對象年齡,每個對象在堅持過一次Minor GC後年齡就會+1,當超過這個參數值時就直接進入老年代
UseAdaptiveSizePolicy 動態調整Java堆中各個區域的大小以及進入老年代的年齡
HandlePromotionFailure 是否允許分配擔保失敗,既老年代的剩餘空間不足以應付新生代的所有存活對象的極端情況
ParallelGCThreads 設置並行GC時進行內存回收的線程數

內存分配與回收策略


  • 對象優先在Eden分配
  • 大對象直接進入老年代
  • 長期存活的對象將進入老年代
  • 動態對象年齡判定,當Surivivor中相同年齡對象大小的總和大於Surivivor空間的一半,年齡大於等於該年齡的對象直接進入老年代,不需要等到年齡閾值
  • 空間分配擔保
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章