您好,登錄后才能下訂單哦!
這篇文章主要講解了“Java垃圾收集器的介紹以及JVM調優方法”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Java垃圾收集器的介紹以及JVM調優方法”吧!
1.Serial
關注點:stw的時間 復制算法 STW
2.ParNew
關注點:stw的時間 Serial的多線程版本 復制算法 STW
參數 -XX:ParallelGCThreads 并行線程數
3.Parallel Scavenge
關注點:達到一個可控制的吞吐量(吞吐量優先) 復制算法 所謂吞吐量就是CPU運行用戶代碼的時間和CPU消耗的時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)。
Paralled Scavenge 收集器提供了兩個參數用于精確控制吞吐量。分別是控制最大垃圾收集停頓時間的-XX:MaxGCPauseMillis ,以及直接設置吞吐量大小的-XX:GCTimeRatio 參數。
參數
-XX:MaxGCPauseMillis -XX:GCTimeRatio -XX:+UseAdaptiveSizePolicy 自適應調節策略
old區
1.Serial Old
Serial的老年代版本 JDK1.5之前可搭配Parallel Scavenge使用 在CMS發生Concurrent mode failure時使用 Mark-Compact
2.Parallel Old
Parallel Scavenge的老年代版本 JDK1.6出現 搭配Parallel Scavenge使用 Mark-Compact
3.CMS
-XX:+UseConcMarkSweepGC Mark-Sweep 過程:初始標記 并發標記 重新標記 并發清除 初始標記和重新標記會STW 耗時最長是并發標記和并發清除 默認回收線程數:CMS默認啟動的回收線程數目是 (ParallelGCThreads + 3)/4),可以通過-XX:ParallelCMSThreads=20來設定 優點:并發低停頓 缺點: 1、對CPU資源非常敏感 占用部分線程(CPU資源)導致應用程序變慢,吞吐量降低 默認線程數=(CPU數量+3)/4 2、無法處理浮動垃圾,可能出現Concurrent mode failure而導致另一次fullGC的產生 參數-XX:CMSInitiatingOccupancyFraction默認68% CMS觸發百分比,CMS執行期間,預留空間無法滿足,就會出現Concurrent mode failure失敗,啟動后備SerialOld的方案,停頓時間更長了。(所以CMSInitiatingOccupancyFraction不能設置的過大) 3、內存碎片 參數-XX:+UseCMSCompactAtFullCollection(停頓時間加長) 默認開啟 -XX:CMSFullGCsBeforeCompaction(執行多少次不進行碎片整理的FullGC后進行一次帶壓縮的) 其他參數 初始標記的并行化-XX:+CMSParallelInitialMarkEnabled 為了減少第二次暫停的時間,開啟并行remark: -XX:+CMSParallelRemarkEnabled,如果remark還是過長的話,可以開啟-XX:+CMSScavengeBeforeRemark(在CMS GC前啟動一次ygc,目的在于減少old gen對ygc gen的引用,降低remark時的開銷-----一般CMS的GC耗時 80%都在remark階段) 為了避免Perm區滿引起的full gc,建議開啟CMS回收Perm區選項: +CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC:設置年老代為并發收集。測試中配置這個以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此時年輕代大小最好用-Xmn設置。 -XX:+UseParNewGC:設置年輕代為并行收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,所以無需再設置此值。
G1收集器
-XX:+UseSerialGC Serial+SerialOld -XX:+UseParNewGC ParNew+SerialOld -XX:+UseParallelGC ParallelScavenge+SerialOld -XX:+UseConcMarkSweepGC ParNew+CMS+SerialOld FullGC算法:單線程的Mark Sweep Compact(MSC) -XX:+UseParallelOldGC ParallelScavenge+Parallel Old FullGC算法:PS MarkSweep
垃圾回收參數 -Xnoclassgc 是否對類進行回收 -verbose:class -XX:+TraceClassUnloading 查看類加載和卸載信息 -XX:SurvivorRatio Eden和其中一個survivor的比值 -XX:PretenureSizeThreshold 大對象進入老年代的閾值,Serial和ParNew生效 -XX:MaxTenuringThreshold 晉升老年代的對象年齡,默認15, CMS默認是4 -XX:HandlePromotionFailure 老年代擔保 -XX:+UseAdaptiveSizePolicy動態調整Java堆中各個區域大小和進入老年代年齡 -XX:ParallelGCThreads 并行回收的線程數 -XX:MaxGCPauseMillis Parallel Scavenge參數,設置GC的最大停頓時間 -XX:GCTimeRatio Parallel Scavenge參數,GC時間占總時間的比率,默認99%,即1%的GC時間 -XX:CMSInitiatingOccupancyFraction,old區觸發cms閾值,默認68% -XX:+UseCMSCompactAtFullCollection(CMS完成后是否進行一次碎片整理,停頓時間加長) -XX:CMSFullGCsBeforeCompaction(執行多少次不進行碎片整理的FullGC后進行一次帶壓縮的) -XX:+ScavengeBeforeFullGC,在fullgc前觸發一次minorGC 垃圾回收統計信息 -XX:+PrintGC 輸出GC日志 -verbose:gc等同于上面那個 -XX:+PrintGCDetails 輸出GC的詳細日志 堆大小設置 -Xmx:最大堆大小 -Xms:初始堆大小(最小內存值) -Xmn:年輕代大小 -XX:NewSize和-XX:MaxNewSize 新生代大小 -XX:SurvivorRatio:3 意思是年輕代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區占整個年輕代的1/5 -XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5 -Xss棧容量 默認256k -XX:PermSize永久代初始值 -XX:MaxPermSize 永久代最大值
生產環境參數配置(CMS-GC)
Java < 8 -server -Xms<heap size>[g|m|k] -Xmx<heap size>[g|m|k] -XX:PermSize=<perm gen size>[g|m|k] -XX:MaxPermSize=<perm gen size>[g|m|k] -Xmn<young size>[g|m|k] -XX:+DisableExplicitGC -XX:SurvivorRatio=<ratio> -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=<percent> -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:"<path to log>" -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<path to dump>`date`.hprof -Dsun.net.inetaddr.ttl=<TTL in seconds> -Djava.rmi.server.hostname=<external IP> -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false Java >= 8 -server -Xms<heap size>[g|m|k] -Xmx<heap size>[g|m|k] -XX:MaxMetaspaceSize=<metaspace size>[g|m|k] -Xmn<young size>[g|m|k] -XX:+DisableExplicitGC -XX:SurvivorRatio=<ratio> -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=<percent> -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:"<path to log>" -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<path to dump>`date`.hprof -Dsun.net.inetaddr.ttl=<TTL in seconds> -Djava.rmi.server.hostname=<external IP> -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
DefNew:Serial收集器新生代名稱 - Tenured - Perm ParNew:ParNew收集器新生代名稱 - PSYoungGen:Parallel Scavenge收集器新生代名稱
GC的時間足夠的小
GC的次數足夠的少
發生Full GC的周期足夠的長
前兩個目前是相悖的,要想GC時間小必須要一個更小的堆,要保證GC次數足夠少,必須保證一個更大的堆,我們只能取其平衡。
(1)針對JVM堆的設置,一般可以通過-Xms -Xmx限定其最小、最大值,為了防止垃圾收集器在最小、最大之間收縮堆而產生額外的時間,我們通常把最大、最小設置為相同的值
(2)年輕代和年老代將根據默認的比例(1:2)分配堆內存,可以通過調整二者之間的比率NewRadio來調整二者之間的大小,也可以針對回收代,比如年輕代,通過 -XX:newSize -XX:MaxNewSize來設置其絕對大小。同樣,為了防止年輕代的堆收縮,我們通常會把-XX:newSize -XX:MaxNewSize設置為同樣大小。-XX:PermSize,-XX:MaxPermSize設置為一樣防止老年代收縮。 -XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5 -XX:MaxTenuringThreshold=0:設置垃圾最大年齡。如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代。對于年老代比較多的應用,可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概論。
(3)年輕代和年老代設置多大才算合理?這個我問題毫無疑問是沒有答案的,否則也就不會有調優。我們觀察一下二者大小變化有哪些影響 年輕代老年代設置:整個JVM內存大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m,所以增大年輕代后,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦年輕代配置為整個堆的3/8。
更大的年輕代必然導致更小的年老代,大的年輕代會延長普通GC的周期,但會增加每次GC的時間;小的年老代會導致更頻繁的Full GC
更小的年輕代必然導致更大年老代,小的年輕代會導致普通GC很頻繁,但每次的GC時間會更短;大的年老代會減少Full GC的頻率
如何選擇應該依賴應用程序對象生命周期的分布情況:如果應用存在大量的臨時對象,應該選擇更大的年輕代;如果存在相對較多的持久對象,年老代應該適當增大。但很多應用都沒有這樣明顯的特性,在抉擇時應該根據以下兩點:(A)本著Full GC盡量少的原則,讓年老代盡量緩存常用對象,JVM的默認比例1:2也是這個道理 (B)通過觀察應用一段時間,看其他在峰值時年老代會占多少內存,在不影響Full GC的前提下,根據實際情況加大年輕代,比如可以把比例控制在1:1。但應該給年老代至少預留1/3的增長空間
(4)在配置較好的機器上(比如多核、大內存),可以為年老代選擇并行收集算法: -XX:+UseParallelOldGC ,默認為Serial收集
(5)線程堆棧的設置:每個線程默認會開啟1M的堆棧,用于存放棧幀、調用參數、局部變量等,對大多數應用而言這個默認值太了,一般256K就足用。理論上,在內存不變的情況下,減少每個線程的堆棧,可以產生更多的線程,但這實際上還受限于操作系統。 -Xss256k:設置每個線程的堆棧大小。JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。更具應用的線程所需內存大小進行調整。在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。
(6)可以通過下面的參數打Heap Dump信息
-XX:HeapDumpPath -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/aaa/dump/heap_trace.txt 通過下面參數可以控制OutOfMemoryError時打印堆的信息 -XX:+HeapDumpOnOutOfMemoryError
注:通過分析dump文件可以發現,每個1小時都會發生一次Full GC,經過多方求證,只要在JVM中開啟了JMX服務,JMX將會1小時執行一次Full GC以清除引用.
jps
-m 主類的參數 -l 主類的全名,如果執行的是jar包,輸出jar路徑 -v 虛擬機參數
jstat
監視虛擬機運行狀態信息,包括類裝載、GC、運行期編譯(JIT) 用于輸出java程序內存使用情況,包括新生代、老年代、元數據區容量、垃圾回收情況 jstat -gcutil 52670 2000 5 進程號 2s輸出一次一共5次 S0:幸存1區當前使用比例 S1:幸存2區當前使用比例 E:伊甸園區使用比例 O:老年代使用比例 M:元數據區使用比例 CCS:壓縮使用比例 YGC:年輕代垃圾回收次數 YGCT:年輕代垃圾回收消耗時間 FGC:老年代垃圾回收次數 FGCT:老年代垃圾回收消耗時間 GCT:垃圾回收消耗總時間
jmap
jmap:用于生成堆轉儲快照。一般稱為dump或heapdump文件。 jmap -histo 3618 上述命令打印出進程ID為3618的內存情況,包括有哪些對象,對象的數量。但我們常用的方式是將指定進程的內存heap輸出到外 部文件,再由專門的heap分析工具進行分析,例如mat(Memory Analysis Tool),所以我們常用的命令是: jmap -dump:live,format=b,file=heap.hprof 3618 -F 強制生成dump快照
jstack
jstack:用戶輸出虛擬機當前時刻的線程快照,常用于定位因為某些線程問題造成的故障或性能問題。一般稱為threaddump文件。 參數 -F當正常輸出沒有響應的時候強制打印棧信息,一般情況不需要使用 -l長列表. 打印關于鎖的附加信息,一般情況不需要使用 -m 如果調用本地方法的話,可打印c/c++的堆棧
jinfo
查看和修改虛擬機參數
可視化工具 JConsole Java監視與管理控制臺 VisualVM 多合一故障處理工具
Java Class文件類型前綴
Element Type Encoding boolean Z byte B char C double D float F int I long J short S class or interface Lclassname; [L代表了相應類型數組嵌套的層數
1.系統崩潰前的一些現象:
每次垃圾回收的時間越來越長,由之前的10ms延長到50ms左右,FullGC的時間也有之前的0.5s延長到4、5s
FullGC的次數越來越多,最頻繁時隔不到1分鐘就進行一次FullGC
年老代的內存越來越大并且每次FullGC后年老代沒有內存被釋放
之后系統會無法響應新的請求,逐漸到達OutOfMemoryError的臨界值。
2.生成堆的dump文件 通過JMX的MBean生成當前的Heap信息,大小為一個3G(整個堆的大小)的hprof文件,如果沒有啟動JMX可以通過Java的jmap命令來生成該文件。
3.分析dump文件
下面要考慮的是如何打開這個3G的堆信息文件,顯然一般的Window系統沒有這么大的內存,必須借助高配置的Linux。當然我們可以借助X-Window把Linux上的圖形導入到Window。我們考慮用下面幾種工具打開該文件:
1.Visual VM 2.IBM HeapAnalyzer 3.JDK 自帶的Hprof工具
使用這些工具時為了確保加載速度,建議設置最大內存為6G。使用后發現,這些工具都無法直觀地觀察到內存泄漏,Visual VM雖能觀察到對象大小,但看不到調用堆棧;HeapAnalyzer雖然能看到調用堆棧,卻無法正確打開一個3G的文件。因此,我們又選用了Eclipse專門的靜態內存分析工具:Mat。
4.分析內存泄漏
通過Mat我們能清楚地看到,哪些對象被懷疑為內存泄漏,哪些對象占的空間最大及對象的調用關系。針對本案,在ThreadLocal中有很多的JbpmContext實例,經過調查是JBPM的Context沒有關閉所致。
另,通過Mat或JMX我們還可以分析線程狀態,可以觀察到線程被阻塞在哪個對象上,從而判斷系統的瓶頸。
5.回歸問題
Q:為什么崩潰前垃圾回收的時間越來越長?
A:根據內存模型和垃圾回收算法,垃圾回收分兩部分:內存標記、清除(復制),標記部分只要內存大小固定,時間是不變的,變的是復制部分,因為每次垃圾回收都有一些回收不掉的內存,所以增加了復制量,導致時間延長。所以,垃圾回收的時間也可以作為判斷內存泄漏的依據
Q:為什么Full GC的次數越來越多?
A:因此內存的積累,逐漸耗盡了年老代的內存,導致新對象分配沒有更多的空間,從而導致頻繁的垃圾回收
Q:為什么年老代占用的內存越來越大?
A:因為年輕代的內存無法被回收,越來越多地被Copy到年老代
一切都是為了這一步,調優,在調優之前,我們需要記住下面的原則:
1、多數的Java應用不需要在服務器上進行GC優化;
2、多數導致GC問題的Java應用,都不是因為我們參數設置錯誤,而是代碼問題;
3、在應用上線之前,先考慮將機器的JVM參數設置到最優(最適合)
4、減少創建對象的數量;
5、減少使用全局變量和大對象;
6、GC優化是到最后不得已才采用的手段;
7、在實際使用中,分析GC情況優化代碼比優化GC參數要多得多;
GC優化的目的有兩個
1、將轉移到老年代的對象數量降低到最小;
2、減少full GC的執行時間;
為了達到上面的目的,一般地,你需要做的事情有:
1、減少使用全局變量和大對象;
2、調整新生代的大小到最合適;
3、設置老年代的大小為最合適;
4、選擇合適的GC收集器
感謝各位的閱讀,以上就是“Java垃圾收集器的介紹以及JVM調優方法”的內容了,經過本文的學習后,相信大家對Java垃圾收集器的介紹以及JVM調優方法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。