目錄 jvm系列(一):jvm記憶體區域與溢出jvm系列(二):垃圾收集器與記憶體分配策略回顧 上文介紹了jvm的記憶體區域以及介紹了記憶體的溢出情況。
jvm區域分為5個,
執行緒獨有:虛擬機器棧,
本地方法棧,
程式計數器。
執行緒共用:方法區,
堆兩種溢出:棧溢出(StackOverflowError),
OutOfMemoryError(OOM)為什麼學習垃圾收集 看起來jvm好像一切幫你做好,
但是當垃圾收集成為系統達到更高併發量的瓶頸時,
我們就需要對這種自動化的技術進行監控和調節。
根據實際應用需求,
選擇最優的收集方式才能更高的性能。
垃圾收集的區域 虛擬機器棧,
本地方法棧,
程式計數器是執行緒私有的,
和執行緒同生共死,
當執行緒銷毀時,
記憶體自然回收,
所以這部分不是考慮的重點。
所以研究重點應該在方法區和堆,
而方法區的回收效率較低,
重點在堆。
確定回收物件引用計數 物件有一個引用計數器 new String有引用,
計數器加一,
String jiajun=new String引用失效,
計數器減一,
jiajun=null;
出現一個問題
Person jia=new Persoon; Person jun=new Person; jia.bro=jun; jun.bro=jia; jia=null; jun=null;
此時雖然我們沒有辦法用jia和jun這兩個物件,
也就是這個是垃圾了,
但是兩個物件又互相引用,
如果用這種演算法是無法進行回收
可達性分析 GC Roots的物件:虛擬機器棧中引用的物件,
本地方法棧中引用的物件,
方法區中類靜態屬性引用的物件,
方法區中常量應用的物件通過GC Roots物件作為起點,
當GC Roots到一個物件沒有引用鏈的話,
那麼就證明這個而物件不可用
垃圾收集演算法標記清除 確定回收物件,
進行標記,
然後回收標記的物件一個問題是標記清除效率不高一個問題是標記清除後產生大量不連續的記憶體碎片,
但需要分配較大物件的時候,
因為沒有足夠大的連續空間分配給此對象,
此時會觸發另一次垃圾收集
複製 解決記憶體碎片的問題將記憶體分為等大的A區和B區,
創建對象時,
存放于A區,
當A區空間用完之後,
將A區存活的物件複製到B區,
然後A區清空,
這樣的話,
就避免了記憶體碎片的問題但是又有一個問題,
這個時候我們只用到了一半的空間,
所以我們要向辦法提高空間利用率研究發現,
新生代物件大多都是朝生夕死,
也就是存活的物件不多,
那麼我們就沒必要分配一半的空間用於“粘貼”所以虛擬機器將新生代分為Eden和Surivior兩個區,
比例為8:1:1,
可用的記憶體為一個伊甸區個一個存活區,
一個存活區用於“粘貼,”,
也就是新生代可用記憶體空間為容量的90%,
這樣的話就提高了空間了利用率但是又有一個問題,
如果這一個存活區存放不了複製的的物件,
那麼怎麼辦?於是,
如果這些物件放不下,
將直接進入另一塊區域老年代
標記整理 對於朝生夕死的新生代來說,
複製演算法是不錯的選擇。
但是對於存活率高的物件不是很好的選擇,
因為要進行較多的複製操作,
效率會降低過程:確定回收物件,
進行標記,
讓存活的物件向一端移動,
然後清理掉端邊界以外的記憶體那麼這樣的話,
不會有記憶體碎片的問題,
也沒有複製過多效率降低的問題
分代收集 上面幾種方法的綜合利用根據新生代和老年代的特點,分別選用適用的演算法。新生代存活率,那麼可以選擇複製演算法老年代存活率高,可以採用標記清理或標記整理演算法垃圾收集器Stop the world(STW) 意思指GC時,停頓所有java執行執行緒因為在進行可達性分析時候,如果同時物件引用進行變化,那麼這樣可達性分析就不正確。這是stop the world 的重要原因Serial收集器 用一條執行緒去完成垃圾收集工作垃圾收集時,需要暫停其他工作執行緒,顯然這是不好,所以縮短執行緒停頓的時間是一個研究重點採用複製演算法,用於新生代Serial Old收集器 與Serial收集器類似採用標記整理演算法,用於老年代ParNew收集器 用多條執行緒去完成收集工作垃圾收集時,需要暫停其他工作執行緒預設開啟的收集執行緒數和cpu數量一樣,ParallelGCThreads參數可以用來設置執行緒數用於新生代,複製演算法Parallel Scavenge收集器 新生代收集器,使用複製演算法關注輸送量,輸送量=代碼執行時間/(代碼執行時間+垃圾收集時間),也就是高效率利用cpu時間,儘快完成程式的運算任務MaxGCPauseMillis參數設置最大停頓時間,GCTimeRatio設置輸送量大小Parllel Old收集器 Parallel Scavenge的老年代版使用標記整理演算法CMS收集器 用於老年代關注停頓時間,希望獲取最短回收停頓時間基於標記清除演算法,會產生記憶體碎片總體來說和使用者執行緒一起併發執行對cpu資源敏感,也就是會佔用較多cpu資源無法處理浮動垃圾,由於是和用戶執行緒併發執行,所以併發時用戶執行緒產生的的新垃圾(浮動垃圾)無法收集。G1收集器 使用多個cpu來縮短STW(stop the world )的停頓時間分代收集不會產生記憶體空間碎片可以建立可預測的停頓時間模型,能夠讓使用者指定M毫秒時間段內,垃圾收集的時間不超過N毫秒流行的收集器組合 新生代老年代 SerialSerial Old SerialCMS ParNewCMS ParNewSerial Old Parallel ScavengeSerial Old Parallel ScavengeParallel Old G1G1垃圾收集參數總結 參數描述 UseSerialGC虛擬機器運行在Client 模式下的預設值,打開此開關後,使用Serial +Serial Old 的收集器組合進行記憶體回收 UseParNewGC打開此開關後,使用ParNew + Serial Old 的收集器組合進行記憶體回收 UseConcMarkSweepGC打開此開關後,使用ParNew + CMS + Serial Old 的收集器組合進行記憶體回收。Serial Old 收集器將作為CMS 收集器出現Concurrent Mode Failure失敗後的後備收集器使用 UseParallelGC虛擬機器運行在Server 模式下的預設值,打開此開關後,使用ParallelScavenge + Serial Old(PS MarkSweep)的收集器組合進行記憶體回收 UseParallelOldGC打開此開關後,使用Parallel Scavenge + Parallel Old 的收集器組合進行記憶體回收 SurvivorRatio新生代中Eden 區域與Survivor 區域的容量比值, 預設為8, 代表Eden :Survivor=8∶1 PretenureSizeThreshold直接晉升到老年代的物件大小,設置這個參數後,大於這個參數的物件將直接在老年代分配 MaxTenuringThreshold晉升到老年代的物件年齡。每個物件在堅持過一次Minor GC 之後,年齡就加1,當超過這個參數值時就進入老年代 UseAdaptiveSizePolicy動態調整Java 堆中各個區域的大小以及進入老年代的年齡 HandlePromotionFailure是否允許分配擔保失敗,即老年代的剩餘空間不足以應付新生代的整個Eden 和Survivor 區的所有物件都存活的極端情況 ParallelGCThreads設置並行GC 時進行記憶體回收的執行緒數 GCTimeRatioGC 時間占總時間的比率,預設值為99,即允許1% 的GC 時間。僅在使用Parallel Scavenge 收集器時生效 MaxGCPauseMillis設置GC 的最大停頓時間。僅在使用Parallel Scavenge 收集器時生效 CMSInitiatingOccupancyFraction設置CMS 收集器在老年代空間被使用多少後觸發垃圾收集。預設值為68%,僅在使用CMS 收集器時生效 UseCMSCompactAtFullCollection設置CMS 收集器在完成垃圾收集後是否要進行一次記憶體磁碟重組。僅在使用CMS 收集器時生效 CMSFullGCsBeforeCompaction設置CMS 收集器在進行若干次垃圾收集後再啟動一次記憶體磁碟重組。僅在使用CMS 收集器時生效記憶體分配與回收策略GC種類 Minor GC,新生代GC,回收速度快Major GC/ Full GC ,老年代GC,速度比Minor GC 慢10倍以上物件優先分配在新生代中的Eden 大多數情況下,物件分配在Eden區,如果Eden區空間不夠,將發起一次MinorGC大物件直接進入老年代 大物件指需要大量連續記憶體空間的物件經常出現大物件,由於需要連續的空間,容易導致記憶體還有不少空間就提前觸發垃圾收集通過PretenureSizeThreshold參數設置限制,超過這個限制的物件直接分配在老年代長時間存活的物件將進入老年代 每個物件有一個物件年齡計數器,經過第一次MinorGC進入存貨區,年齡為一,以後經過MinorGC還能繼續在存活區的話,年齡加一,當通過限制(MaxTenuringThreshold)後會晉升到老年代動態物件年齡判定 存活區空間相同年齡所有物件大小的總和大於存活區的空間一半,大於等於該年齡的可以直接進入老年代空間分配擔保 Minor GC之前,虛擬機器檢查老年代最大可用的連續空間是否大於新生代所有物件總空間,如果可以,可以安全進行如果不行,看是否允許擔保失敗,不允許的話進行Full GC允許的話,看老年代的最大可用連續空間是否大於歷次晉升老年代物件的平均大小(將之前的作為經驗值),如果大於,進行MinorGC,否則進行FullGC方法區回收回收內容 廢棄常量無用的類回收的效率不高廢棄常量 沒有任何地方引用常量池的字串常量,必要的話,這個字串會被清除常量池。常量池中的類 方法 欄位的符號引用也是類似無用的類 不存在該類的任何物件載入該類的ClassLoader已經被回收該類的對應的Class物件沒有被引用,無法在任何地方通過反射訪問該類的方法我覺得分享是一種精神,分享是我的樂趣所在,不是說我覺得我講得一定是對的,我講得可能很多是不對的,但是我希望我講的東西是我人生的體驗和思考,是給很多人反思,也許給你一秒鐘、半秒鐘,哪怕說一句話有點道理,引發自己內心的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)
分代收集 上面幾種方法的綜合利用根據新生代和老年代的特點,分別選用適用的演算法。新生代存活率,那麼可以選擇複製演算法老年代存活率高,可以採用標記清理或標記整理演算法垃圾收集器Stop the world(STW) 意思指GC時,停頓所有java執行執行緒因為在進行可達性分析時候,如果同時物件引用進行變化,那麼這樣可達性分析就不正確。這是stop the world 的重要原因Serial收集器 用一條執行緒去完成垃圾收集工作垃圾收集時,需要暫停其他工作執行緒,顯然這是不好,所以縮短執行緒停頓的時間是一個研究重點採用複製演算法,用於新生代Serial Old收集器 與Serial收集器類似採用標記整理演算法,用於老年代ParNew收集器 用多條執行緒去完成收集工作垃圾收集時,需要暫停其他工作執行緒預設開啟的收集執行緒數和cpu數量一樣,ParallelGCThreads參數可以用來設置執行緒數用於新生代,複製演算法Parallel Scavenge收集器 新生代收集器,使用複製演算法關注輸送量,輸送量=代碼執行時間/(代碼執行時間+垃圾收集時間),也就是高效率利用cpu時間,儘快完成程式的運算任務MaxGCPauseMillis參數設置最大停頓時間,GCTimeRatio設置輸送量大小Parllel Old收集器 Parallel Scavenge的老年代版使用標記整理演算法CMS收集器 用於老年代關注停頓時間,希望獲取最短回收停頓時間基於標記清除演算法,會產生記憶體碎片總體來說和使用者執行緒一起併發執行對cpu資源敏感,也就是會佔用較多cpu資源無法處理浮動垃圾,由於是和用戶執行緒併發執行,所以併發時用戶執行緒產生的的新垃圾(浮動垃圾)無法收集。G1收集器 使用多個cpu來縮短STW(stop the world )的停頓時間分代收集不會產生記憶體空間碎片可以建立可預測的停頓時間模型,能夠讓使用者指定M毫秒時間段內,垃圾收集的時間不超過N毫秒流行的收集器組合 新生代老年代 SerialSerial Old SerialCMS ParNewCMS ParNewSerial Old Parallel ScavengeSerial Old Parallel ScavengeParallel Old G1G1垃圾收集參數總結 參數描述 UseSerialGC虛擬機器運行在Client 模式下的預設值,打開此開關後,使用Serial +Serial Old 的收集器組合進行記憶體回收 UseParNewGC打開此開關後,使用ParNew + Serial Old 的收集器組合進行記憶體回收 UseConcMarkSweepGC打開此開關後,使用ParNew + CMS + Serial Old 的收集器組合進行記憶體回收。Serial Old 收集器將作為CMS 收集器出現Concurrent Mode Failure失敗後的後備收集器使用 UseParallelGC虛擬機器運行在Server 模式下的預設值,打開此開關後,使用ParallelScavenge + Serial Old(PS MarkSweep)的收集器組合進行記憶體回收 UseParallelOldGC打開此開關後,使用Parallel Scavenge + Parallel Old 的收集器組合進行記憶體回收 SurvivorRatio新生代中Eden 區域與Survivor 區域的容量比值, 預設為8, 代表Eden :Survivor=8∶1 PretenureSizeThreshold直接晉升到老年代的物件大小,設置這個參數後,大於這個參數的物件將直接在老年代分配 MaxTenuringThreshold晉升到老年代的物件年齡。每個物件在堅持過一次Minor GC 之後,年齡就加1,當超過這個參數值時就進入老年代 UseAdaptiveSizePolicy動態調整Java 堆中各個區域的大小以及進入老年代的年齡 HandlePromotionFailure是否允許分配擔保失敗,即老年代的剩餘空間不足以應付新生代的整個Eden 和Survivor 區的所有物件都存活的極端情況 ParallelGCThreads設置並行GC 時進行記憶體回收的執行緒數 GCTimeRatioGC 時間占總時間的比率,預設值為99,即允許1% 的GC 時間。僅在使用Parallel Scavenge 收集器時生效 MaxGCPauseMillis設置GC 的最大停頓時間。僅在使用Parallel Scavenge 收集器時生效 CMSInitiatingOccupancyFraction設置CMS 收集器在老年代空間被使用多少後觸發垃圾收集。預設值為68%,僅在使用CMS 收集器時生效 UseCMSCompactAtFullCollection設置CMS 收集器在完成垃圾收集後是否要進行一次記憶體磁碟重組。僅在使用CMS 收集器時生效 CMSFullGCsBeforeCompaction設置CMS 收集器在進行若干次垃圾收集後再啟動一次記憶體磁碟重組。僅在使用CMS 收集器時生效記憶體分配與回收策略GC種類 Minor GC,新生代GC,回收速度快Major GC/ Full GC ,老年代GC,速度比Minor GC 慢10倍以上物件優先分配在新生代中的Eden 大多數情況下,物件分配在Eden區,如果Eden區空間不夠,將發起一次MinorGC大物件直接進入老年代 大物件指需要大量連續記憶體空間的物件經常出現大物件,由於需要連續的空間,容易導致記憶體還有不少空間就提前觸發垃圾收集通過PretenureSizeThreshold參數設置限制,超過這個限制的物件直接分配在老年代長時間存活的物件將進入老年代 每個物件有一個物件年齡計數器,經過第一次MinorGC進入存貨區,年齡為一,以後經過MinorGC還能繼續在存活區的話,年齡加一,當通過限制(MaxTenuringThreshold)後會晉升到老年代動態物件年齡判定 存活區空間相同年齡所有物件大小的總和大於存活區的空間一半,大於等於該年齡的可以直接進入老年代空間分配擔保 Minor GC之前,虛擬機器檢查老年代最大可用的連續空間是否大於新生代所有物件總空間,如果可以,可以安全進行如果不行,看是否允許擔保失敗,不允許的話進行Full GC允許的話,看老年代的最大可用連續空間是否大於歷次晉升老年代物件的平均大小(將之前的作為經驗值),如果大於,進行MinorGC,否則進行FullGC方法區回收回收內容 廢棄常量無用的類回收的效率不高廢棄常量 沒有任何地方引用常量池的字串常量,必要的話,這個字串會被清除常量池。常量池中的類 方法 欄位的符號引用也是類似無用的類 不存在該類的任何物件載入該類的ClassLoader已經被回收該類的對應的Class物件沒有被引用,無法在任何地方通過反射訪問該類的方法我覺得分享是一種精神,分享是我的樂趣所在,不是說我覺得我講得一定是對的,我講得可能很多是不對的,但是我希望我講的東西是我人生的體驗和思考,是給很多人反思,也許給你一秒鐘、半秒鐘,哪怕說一句話有點道理,引發自己內心的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)