1. 程式人生 > >JVM垃圾回收演算法與引數配置

JVM垃圾回收演算法與引數配置

★引用計數法 
這是個古老而經典的垃圾收集演算法,其核心就是在物件被其他所引用時計數器+1,而當引用失效時-1,但是這種方式有非常嚴重的問題:無法處理迴圈引用的情況,還有就是每次進行加減操作比較浪費系統性能。

★標記清除法 
分為標記和清除兩個階段進行處理記憶體中的物件,當然這種方式也有非常大的弊端,就是空間碎片問題,垃圾回收後的空間不連續,不連續的記憶體空間工作效率低於連續的記憶體空間。

★複製演算法(java堆的新生代from區和to區) 
其核心思想就是將記憶體空間分為2塊,每次只使用其中一塊,在垃圾回收時,將正在使用的記憶體中的存留物件複製到未被使用的記憶體中去,之後清除之前使用的記憶體塊中的所有物件,反覆交換兩個記憶體中的角色,完成垃圾收集。

★標記壓縮法(java堆老年代) 
標記壓縮法在標記清除法上做了優化,把存活的物件壓縮到記憶體一端,然後再進行GC。

★分代演算法 
就是根據物件的特點把記憶體分為N塊,而後根據每個記憶體的特點使用不同的演算法。對於新生代和老年代來說,新生代回收頻率很高,但是每次回收耗時很短,而老年代回收頻率較低,但是會耗時較長,所以應該儘量減少老年代的GC。

★分割槽演算法(jdk1.7後G1) 
其主要就是將整個記憶體分為N多個小的獨立空間,每個小空間都可以獨立使用,這樣細粒度地控制一次回收一些小空間,而不是對整個空間進行GC,從而提升效能,並減少GC的停頓時間。

新生代的from和to區是使用的複製演算法,老年代是使用的標記壓縮演算法。

物件如何進入老年代? 
一般物件建立會被放到eden區,每次GC後年齡+1,當達到一個閾值時會被放到老年代。 
-XX:MaxTenuringThreshold 設定,預設值為15 
特殊:大物件在新生代無法裝入時,會直接進入老年代,可以設定這個大小 
-XX:PretenureSizeThreshold

※虛擬機器對於體積不大的物件 會優先把資料分配到TLAB區域中,因此就失去了在老年代分配的機會 
使用-XX:-UseTLAB 禁用TLAB區域 
下圖是java物件建立時的記憶體選擇過程: 


TLAB 
全稱是Thread Local Allocation Buffer 即執行緒本地分配快取,從名字上看是一個執行緒專用的記憶體分配區域,是為了加速物件分配而產生的,每一個執行緒都會產生一個TLAB,該執行緒獨享的工作區域,JVM使用這種TLAB區來避免多執行緒衝突問題,提高物件分配的效率。TLAB空間一般不會太大,當大物件無法在TLAB分配時,則會直接分配在堆上。 
-XX:+UseTLAB 使用TLAB 
-XX:+TLABSize 設定大小 
-XX:TLABRefillWasterFraction 設定維護進入TLAB空間的單個物件大小,它是一個比例值,預設64,即如果物件大於整個空間的1/64,則在堆建立物件。 
-XX:+PrintTLAB 檢視TLAB資訊 
-XX:ResizeTLAB 自調整TLABRefillWasterFraction閾值

●垃圾收集器

★序列垃圾回收器(新生代和老年代) 
-XX:+UseSerialGC 根據伺服器效能選擇序列還是並行

★Parallel Scavenge垃圾回收器(新生代) 
-XX:UseParallelGC 開啟 -XX:ParallelGCThreads 指定執行緒數,一般等於CPU核數。 
使用複製演算法,有個非常重要的特點,非常關注系統吞吐量,提供了兩個引數控制: 
-XX:MaxGCPauseMillis 設定最大垃圾收集停頓時間,可以把JVM在GC停頓的時間控制在這個範圍內,設定小了

可以減少GC停頓時間,但是會引起GC頻繁,增加了GC總時間,降低吞吐量。 
-XX:GCTimeRatio 設定吞吐量大小,範圍是0-100整數,預設99,那麼系統將花費1/(1+n)的時間用於GC,也就是1/(1+99)=1%的時間。 
另外:-XX:+UseAdaptiveSizePolicy 開啟自適應模式,在這種模式下,新生代大小、eden、from/to比例,以及其他引數會被自動調整,以達到在堆大小,吞吐量和停頓時間之間的平衡點。 
-XX:+UseParallelOldGC 配置老年代GC,使用標記壓縮演算法,-XX:+ParallelGCThreads設定執行緒數

★CMS回收器 
Concurrent Mark Sweep 併發標記清除,使用的標記清除法,主要關注系統停頓時間 
-XX:UseConcMarkSweepGC開啟,-XX:ConcGCThreads設定執行緒數 
CMS不是獨佔的垃圾回收器,在工作過程中,程式仍在不斷工作,垃圾也不斷產生,所以在CMS工作的時候要確保記憶體夠用。CMS不會等到應用程式飽和的時候才去工作,而是在某一個閾值的時候開始回收,可以通過一個引數來指定:-XX:CMSInitiatingOccupancyFraction,預設值為68,意思是當老年代空間使用率達到68%的時候執行。如果記憶體使用率增長過快,CMS沒有足夠的記憶體去執行,這時候CMS會中斷,JVM會啟用老年代序列回收器,但是會導致程式中斷,直到回收完成後才會正常工作,這個過程GC的停頓時間可能會很長,所以這個引數要根據實際情況來設定。 
解決標記清除法的碎片問題,使用-XX:+UseCMSCompactAtFullCollection使CMS完成後整理碎片,-XX:CMSFullGCsBeforeCompaction設定多少次回收後整理碎片。 
注意:CMS無法與Parallel Scavenge收集器一起工作,只能選擇ParNew或者Serial收集器。

★G1回收器 
Garbage-First 是在JDK7之後提出的,長遠目標是取代CMS,屬於分代垃圾回收器,區分新生代和老年代,依然有eden、from、to區,並不要求這些區域的記憶體空間連續,使用的演算法是分割槽演算法。 
並行性:回收期間多執行緒同時工作。 
併發性:與應用程式執行不會發生阻塞。 
分代GC:依然是一個分代GC垃圾回收器,但是它兼顧新生代和老年代一起工作,之前的垃圾回收器只工作在新生代或者老年代。 
空間整理:不會像CMS在若干次GC後需要進行碎片整理,G1採用有效複製物件的方式減少空間碎片。 
可預見性:由於分割槽的原因,G1可以只選取部分割槽域進行回收,縮小了回收的範圍,提升了效能。 
-XX:+UseG1GC 開啟 
-XX:MaxGCPauseMillis 最大停頓時間 
-XX:ParallelGCThreads 執行緒數

-Xloggc:d:/gc.log gc的log存放位置
--------------------- 
作者:Lovnx 
來源:CSDN 
原文:https://blog.csdn.net/rickiyeat/article/details/76726636 
版權宣告:本文為博主原創文章,轉載請附上博文連結!