標籤:
【聲明】
歡迎轉載,但請保留文章原始出處→_→
生命壹號:http://www.cnblogs.com/smyhvae/
文章來源:http://www.cnblogs.com/smyhvae/p/4748313.html
連絡方式:[email protected]
本文主要內容:
零、堆的回顧:
新生代中的98%對象都是“朝生夕死”的,所以並不需要按照1:1的比例來劃分記憶體空間,而是將記憶體分為一塊比較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor。當回收時,將Eden和Survivor中還存活著的對象一次性地複製到另外一塊Survivor空間上,最後清理掉Eden和剛才用過的Survivor空間。HotSpot虛擬機器預設Eden和Survivor的大小比例是8:1,也就是說,每次新生代中可用記憶體空間為整個新生代容量的90%(80%+10%),只有10%的空間會被浪費。
當然,98%的對象可回收只是一般情境下的資料,我們沒有辦法保證每次回收都只有不多於10%的對象存活,當Survivor空間不夠用時,需要依賴於老年代進行分配擔保,所以大對象直接進入老年代。
堆的結構如所示:
垃圾收集器:
如果說收集演算法時記憶體回收的方法論,那麼垃圾收集器就是記憶體回收的具體實現。
雖然我們在對各種收集器進行比較,但並非為了挑出一個最好的收集器。因為直到現在位置還沒有最好的收集器出現,更加沒有萬能的收集器,所以我們選擇的只是對具體應用最合適的收集器。
一、串列收集器:Serial收集器
- 最古老,最穩定
- 簡單而高效
- 可能會產生較長的停頓
- -XX:+UseSerialGC
新生代、老年代都會使用串列回收
新生代複製演算法
老年代標記-整理
總結:Serial收集器對於運行在Client模式下的虛擬機器來說是一個很好的選擇。
這個收集器是一個單線程的收集器,但它的單線程的意義並不僅僅說明它只會使用一個CPU或一條收集線程去完成垃圾收集工作,更重要的是在它進行垃圾收集時,必須暫停其他所有的背景工作執行緒,直到它收集結束。收集器的運行過程如所示:
二、並行收集器:
1、ParNew收集器:
- ParNew收集器其實就是Serial收集器新生代的並行版本。
- 多線程,需要多核支援。
- -XX:+UseParNewGC
新生代並行
老年代串列
- -XX:ParallelGCThreads 限制線程數量
2、Parallel Scanvenge收集器:
- 類似ParNew,但更加關注輸送量
- -XX:+UseParallelGC 使用Parallel Scanvenge收集器:新生代並行,老年代串列
3、Parallel Old收集器:
- Parallel Old收集器是Parallel Scanvenge收集器的老年代版本
- -XX:+UseParallelGC 使用Parallel Old收集器:新生代並行,老年代並行
如所示:
各種參數設定:
最大停頓時間,單位毫秒
GC儘力保證回收時間不超過設定值
0-100的取值範圍
垃圾收集時間佔總時間的比
預設99,即最大允許1%時間做GC
註:這兩個參數是矛盾的。因為停頓時間和輸送量不可能同時調優。我們一方買希望停頓時間少,另外一方面希望輸送量高,其實這是矛盾的。因為:在GC的時候,記憶體回收的工作總量是不變的,如果將停頓時間減少,那頻率就會提高;既然頻率提高了,說明就會頻繁的進行GC,那輸送量就會減少,效能就會降低。
輸送量:CPU用於使用者代碼的時間/CPU總消耗時間的比值,即=運行使用者代碼的時間/(運行使用者代碼時間+垃圾收集時間)。比如,虛擬機器總共運行了100分鐘,其中垃圾收集花掉1分鐘,那輸送量就是99%。
注2:以上所有的收集器當中,當執行GC時,都會stop the world,但是下面的CMS收集器卻不會這樣。
三、CMS收集器:
CMS收集器(Concurrent Mark Sweep:並發標記清除)是一種以擷取最短回收停頓時間為目標的收集器。適合應用在互連網站或者B/S系統的伺服器上,這類應用隔油器重視伺服器的響應速度,希望系統停頓時間最短。
- Concurrent Mark Sweep 並發標記清除,並發低停頓
- 標記-清除演算法
- 並發階段會降低輸送量(因為停頓時間減少了,於是GC的頻率會變高)
- 老年代收集器(新生代使用ParNew)
- -XX:+UseConcMarkSweepGC 開啟這收集器
註:這裡的並髮指的是與使用者線程一起執行。
2、CMS收集器運行過程:(著重實現了標記的過程)
(1)初始標記
根可以直接關聯到的對象
速度快
(2)並發標記(和使用者線程一起)
主要標記過程,標記全部對象
(3)重新標記
由於並發標記時,使用者線程依然運行,因此在正式清理前,再做修正
(4)並發清除(和使用者線程一起)
基於標記結果,直接清理對象
整個過程如所示:
其中,初始標記和重新標記時,需要stop the world。
整個過程中耗時最長的是並發標記和並發清除,這兩個過程都可以和使用者線程一起工作。
列印GC日誌舉例如下:
3、CMS收集器特點:
(1)儘可能降低停頓
(2)會影響系統整體輸送量和效能
比如,在使用者線程運行過程中,分一半CPU去做GC,系統效能在GC階段,反應速度就下降一半
(3)清理不徹底
因為在清理階段,使用者線程還在運行,會產生新的垃圾,無法清理
(4)因為和使用者線程一起運行,不能在空間快滿時再清理
-XX:CMSInitiatingOccupancyFraction設定觸發GC的閾值
如果不幸記憶體預留空間不夠,就會引起concurrent mode failure
我們來看一下concurrent mode failure的日誌:
碰到中的情況,我們需要使用串列收集器作為後備。
4、既然標記清除演算法會造成記憶體空間的片段化,CMS收集器為什麼使用標記清除演算法而不是使用標記整理演算法:
答案:
CMS收集器更加關注停頓,它在做GC的時候是和使用者線程一起工作的(並發執行),如果使用標記整理演算法的話,那麼在清理的時候就會去移動可用對象的記憶體空間,那麼應用程式的線程就很有可能找不到應用對象在哪裡。
為瞭解決片段的問題,CMS收集器會有一些整理上的參數,接下來就來講這個。
5、整理時的各種參數:
- -XX:+ UseCMSCompactAtFullCollection
Full GC後,進行一次整理。整理過程是獨佔的,會引起停頓時間變長
- -XX:+CMSFullGCsBeforeCompaction
設定進行幾次Full GC後,進行一次磁碟重組
設定CMS的線程數量
四、GC參數的整理:
-XX:+UseSerialGC:在新生代和老年代使用串列收集器
-XX:SurvivorRatio:設定eden區大小和survivior區大小的比例
-XX:NewRatio:新生代和老年代的比
-XX:+UseParNewGC:在新生代使用並行收集器
-XX:+UseParallelGC :新生代使用並行回收收集器
-XX:+UseParallelOldGC:老年代使用並行回收收集器
-XX:ParallelGCThreads:設定用於記憶體回收的線程數
-XX:+UseConcMarkSweepGC:新生代使用並行收集器,老年代使用CMS+串列收集器
-XX:ParallelCMSThreads:設定CMS的線程數量
-XX:CMSInitiatingOccupancyFraction:設定CMS收集器在老年代空間被使用多少後觸發
-XX:+UseCMSCompactAtFullCollection:設定CMS收集器在完成垃圾收集後是否要進行一次記憶體片段的整理
-XX:CMSFullGCsBeforeCompaction:設定進行多少次CMS記憶體回收後,進行一次記憶體壓縮
-XX:+CMSClassUnloadingEnabled:允許對類別中繼資料進行回收
-XX:CMSInitiatingPermOccupancyFraction:當永久區佔用率達到這一百分比時,啟動CMS回收
-XX:UseCMSInitiatingOccupancyOnly:表示只在到達閥值的時候,才進行CMS回收
最後的總結:
為了減輕GC壓力,我們需要注意些什嗎?
- 軟體如何設計架構(效能的根本在應用)
- GC參數屬於微調(設定不合理會影響效能,產生大的延時)
- 堆空間如何管理和分配
- 代碼如何寫
參考連結:
http://www.cnblogs.com/zuoxiaolong/p/jvm8.html
Java虛擬機器詳解05----垃圾收集器及GC參數