JVM調(diào)優(yōu)參數(shù)、方法、工具以及案例總結(jié)
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
? 作者?|? 等不到的口琴
來源 |? urlify.cn/bQ3i6v
76套java從入門到精通實(shí)戰(zhàn)課程分享
這種文章挺難寫的,一是JVM參數(shù)巨多,二是內(nèi)容枯燥乏味,但是想理解JVM調(diào)優(yōu)又是沒法避開的環(huán)節(jié),本文主要用來總結(jié)梳理便于以后翻閱,主要圍繞四個(gè)大的方面展開,分別是JVM調(diào)優(yōu)參數(shù)、JVM調(diào)優(yōu)方法(流程)、JVM調(diào)優(yōu)工具、JVM調(diào)優(yōu)案例,調(diào)優(yōu)案例目前正在分析,會(huì)在將來補(bǔ)上。
垃圾回收有關(guān)參數(shù)
參數(shù)部分,這兒只是做一個(gè)總結(jié),更詳細(xì)更新的內(nèi)容請參考Oracle官網(wǎng):JVM的命令行參數(shù)參考
處理器組合參數(shù)
關(guān)于JVM垃圾處理器區(qū)別,參考:JVM調(diào)優(yōu)之垃圾定位、垃圾回收算法、垃圾處理器對比
-XX:+UseSerialGC = Serial New (DefNew) + Serial Old
適用于小型程序。默認(rèn)情況下不會(huì)是這種選項(xiàng),HotSpot會(huì)根據(jù)計(jì)算及配置和JDK版本自動(dòng)選擇收集器
-XX:+UseParNewGC = ParNew + SerialOld
這個(gè)組合已經(jīng)很少用(在某些版本中已經(jīng)廢棄),詳情參考:Why Remove support for ParNew+SerialOld and DefNew+CMS in the future?
-XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old
-XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默認(rèn)) 【PS + SerialOld】
-XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old
-XX:+UseG1GC = G1
Linux中沒找到默認(rèn)GC的查看方法,而windows中會(huì)打印UseParallelGC
java +XX:+PrintCommandLineFlags -version
通過GC的日志來分辨
Linux下1.8版本默認(rèn)的垃圾回收器到底是什么?
1.8.0_181 默認(rèn)(看不出來)Copy MarkCompact
1.8.0_222 默認(rèn) PS + PO
虛擬機(jī)參數(shù)
| -Xms | 初始堆大小 | 物理內(nèi)存的1/64(<1GB) | 默認(rèn)(MinHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存小于40%時(shí),JVM就會(huì)增大堆直到-Xmx的最大限制. |
| -Xmx | 最大堆大小 | 物理內(nèi)存的1/4(<1GB) | 默認(rèn)(MaxHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存大于70%時(shí),JVM會(huì)減少堆直到 -Xms的最小限制 |
| -Xmn | 年輕代大小(1.4or lator) | 注意:此處的大小是(eden+ 2 survivor space).與jmap -heap中顯示的New gen是不同的。整個(gè)堆大小=年輕代大小 + 年老代大小 + 持久代大小. 增大年輕代后,將會(huì)減小年老代大小.此值對系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8 | |
| -XX:NewSize | 設(shè)置年輕代大小(for 1.3/1.4) | ||
| -XX:MaxNewSize | 年輕代最大值(for 1.3/1.4) | ||
| -XX:PermSize | 設(shè)置持久代(perm gen)初始值 | 物理內(nèi)存的1/64 | |
| -XX:MaxPermSize | 設(shè)置持久代最大值 | 物理內(nèi)存的1/4 | |
| -Xss | 每個(gè)線程的堆棧大小 | JDK5.0以后每個(gè)線程堆棧大小為1M,以前每個(gè)線程堆棧大小為256K.更具應(yīng)用的線程所需內(nèi)存大小進(jìn)行 調(diào)整.在相同物理內(nèi)存下,減小這個(gè)值能生成更多的線程.但是操作系統(tǒng)對一個(gè)進(jìn)程內(nèi)的線程數(shù)還是有限制的,不能無限生成,經(jīng)驗(yàn)值在3000~5000左右 一般小的應(yīng)用, 如果棧不是很深, 應(yīng)該是128k夠用的 大的應(yīng)用建議使用256k。這個(gè)選項(xiàng)對性能影響比較大,需要嚴(yán)格的測試。和threadstacksize選項(xiàng)解釋很類似,官方文檔似乎沒有解釋,在論壇中有這樣一句話:"” -Xss is translated in a VM flag named ThreadStackSize” 一般設(shè)置這個(gè)值就可以了。 | |
| -XX:ThreadStackSize | Thread Stack Size | (0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.] | |
| -XX:NewRatio | 年輕代(包括Eden和兩個(gè)Survivor區(qū))與年老代的比值(除去持久代) | -XX:NewRatio=4表示年輕代與年老代所占比值為1:4,年輕代占整個(gè)堆棧的1/5 Xms=Xmx并且設(shè)置了Xmn的情況下,該參數(shù)不需要進(jìn)行設(shè)置。 | |
| -XX:SurvivorRatio | Eden區(qū)與Survivor區(qū)的大小比值 | 設(shè)置為8,則兩個(gè)Survivor區(qū)與一個(gè)Eden區(qū)的比值為2:8,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/10 | |
| -XX:LargePageSizeInBytes | 內(nèi)存頁的大小不可設(shè)置過大, 會(huì)影響Perm的大小 | =128m | |
| -XX:+UseFastAccessorMethods | 原始類型的快速優(yōu)化 | ||
| -XX:+DisableExplicitGC | 關(guān)閉System.gc() | 這個(gè)參數(shù)需要嚴(yán)格的測試 | |
| -XX:MaxTenuringThreshold | 垃圾最大年齡 | 如果設(shè)置為0的話,則年輕代對象不經(jīng)過Survivor區(qū),直接進(jìn)入年老代. 對于年老代比較多的應(yīng)用,可以提高效率.如果將此值設(shè)置為一個(gè)較大值,則年輕代對象會(huì)在Survivor區(qū)進(jìn)行多次復(fù)制,這樣可以增加對象再年輕代的存活 時(shí)間,增加在年輕代即被回收的概率 該參數(shù)只有在串行GC時(shí)才有效. | |
| -XX:+AggressiveOpts | 加快編譯 | ||
| -XX:+UseBiasedLocking | 鎖機(jī)制的性能改善 | ||
| -Xnoclassgc | 禁用垃圾回收 | ||
| -XX:SoftRefLRUPolicyMSPerMB | 每兆堆空閑空間中SoftReference的存活時(shí)間 | 1s | softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap |
| -XX:PretenureSizeThreshold | 對象超過多大是直接在舊生代分配 | 0 | 單位字節(jié) 新生代采用Parallel Scavenge GC時(shí)無效 另一種直接在舊生代分配的情況是大的數(shù)組對象,且數(shù)組中無外部引用對象. |
| -XX:TLABWasteTargetPercent | TLAB占eden區(qū)的百分比 | 1% | |
| -XX:+CollectGen0First | FullGC時(shí)是否先YGC | false |
并行收集器相關(guān)參數(shù)
| -XX:+UseParallelGC | Full GC采用parallel MSC (此項(xiàng)待驗(yàn)證) | 選擇垃圾收集器為并行收集器.此配置僅對年輕代有效.即上述配置下,年輕代使用并發(fā)收集,而年老代仍舊使用串行收集.(此項(xiàng)待驗(yàn)證) | |
| -XX:+UseParNewGC | 設(shè)置年輕代為并行收集 | 可與CMS收集同時(shí)使用 JDK5.0以上,JVM會(huì)根據(jù)系統(tǒng)配置自行設(shè)置,所以無需再設(shè)置此值 | |
| -XX:ParallelGCThreads | 并行收集器的線程數(shù) | 此值最好配置與處理器數(shù)目相等 同樣適用于CMS | |
| -XX:+UseParallelOldGC | 年老代垃圾收集方式為并行收集(Parallel Compacting) | 這個(gè)是JAVA 6出現(xiàn)的參數(shù)選項(xiàng) | |
| -XX:MaxGCPauseMillis | 每次年輕代垃圾回收的最長時(shí)間(最大暫停時(shí)間) | 如果無法滿足此時(shí)間,JVM會(huì)自動(dòng)調(diào)整年輕代大小,以滿足此值. | |
| -XX:+UseAdaptiveSizePolicy | 自動(dòng)選擇年輕代區(qū)大小和相應(yīng)的Survivor區(qū)比例 | 設(shè)置此選項(xiàng)后,并行收集器會(huì)自動(dòng)選擇年輕代區(qū)大小和相應(yīng)的Survivor區(qū)比例,以達(dá)到目標(biāo)系統(tǒng)規(guī)定的最低相應(yīng)時(shí)間或者收集頻率等,此值建議使用并行收集器時(shí),一直打開. | |
| -XX:GCTimeRatio | 設(shè)置垃圾回收時(shí)間占程序運(yùn)行時(shí)間的百分比 | 公式為1/(1+n) | |
| -XX:+ScavengeBeforeFullGC | Full GC前調(diào)用YGC | true | Do young generation GC prior to a full GC. (Introduced in 1.4.1.) |
CMS處理器參數(shù)設(shè)置
| -XX:+UseConcMarkSweepGC | 使用CMS內(nèi)存收集 | 測試中配置這個(gè)以后,-XX:NewRatio=4的配置失效了,原因不明.所以,此時(shí)年輕代大小最好用-Xmn設(shè)置.??? | |
| -XX:+AggressiveHeap | 試圖是使用大量的物理內(nèi)存 長時(shí)間大內(nèi)存使用的優(yōu)化,能檢查計(jì)算資源(內(nèi)存, 處理器數(shù)量) 至少需要256MB內(nèi)存 大量的CPU/內(nèi)存, (在1.4.1在4CPU的機(jī)器上已經(jīng)顯示有提升) | ||
| -XX:CMSFullGCsBeforeCompaction | 多少次后進(jìn)行內(nèi)存壓縮 | 由于并發(fā)收集器不對內(nèi)存空間進(jìn)行壓縮,整理,所以運(yùn)行一段時(shí)間以后會(huì)產(chǎn)生"碎片",使得運(yùn)行效率降低.此值設(shè)置運(yùn)行多少次GC以后對內(nèi)存空間進(jìn)行壓縮,整理. | |
| -XX:+CMSParallelRemarkEnabled | 降低標(biāo)記停頓 | ||
| -XX+UseCMSCompactAtFullCollection | 在FULL GC的時(shí)候, 對年老代的壓縮 | CMS是不會(huì)移動(dòng)內(nèi)存的, 因此, 這個(gè)非常容易產(chǎn)生碎片, 導(dǎo)致內(nèi)存不夠用, 因此, 內(nèi)存的壓縮這個(gè)時(shí)候就會(huì)被啟用。增加這個(gè)參數(shù)是個(gè)好習(xí)慣??赡軙?huì)影響性能,但是可以消除碎片 | |
| -XX:+UseCMSInitiatingOccupancyOnly | 使用手動(dòng)定義初始化定義開始CMS收集 | 禁止hostspot自行觸發(fā)CMS GC | |
| -XX:CMSInitiatingOccupancyFraction=70 | 使用cms作為垃圾回收 使用70%后開始CMS收集 | 92 | 為了保證不出現(xiàn)promotion failed(見下面介紹)錯(cuò)誤,該值的設(shè)置需要滿足以下公式CMSInitiatingOccupancyFraction計(jì)算公式 |
| -XX:CMSInitiatingPermOccupancyFraction | 設(shè)置Perm Gen使用到達(dá)多少比率時(shí)觸發(fā) | 92 | |
| -XX:+CMSIncrementalMode | 設(shè)置為增量模式 | 用于單CPU情況 | |
| -XX:+CMSClassUnloadingEnabled |
JVM輔助信息參數(shù)設(shè)置
| -XX:+PrintGC | 輸出形式:[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs] | ||
| -XX:+PrintGCDetails | 輸出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs] | ||
| -XX:+PrintGCTimeStamps | |||
| -XX:+PrintGC:PrintGCTimeStamps | 可與-XX:+PrintGC -XX:+PrintGCDetails混合使用 輸出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs] | ||
| -XX:+PrintGCApplicationStoppedTime | 打印垃圾回收期間程序暫停的時(shí)間.可與上面混合使用 | 輸出形式:Total time for which application threads were stopped: 0.0468229 seconds | |
| -XX:+PrintGCApplicationConcurrentTime | 打印每次垃圾回收前,程序未中斷的執(zhí)行時(shí)間.可與上面混合使用 | 輸出形式:Application time: 0.5291524 seconds | |
| -XX:+PrintHeapAtGC | 打印GC前后的詳細(xì)堆棧信息 | ||
| -Xloggc:filename | 把相關(guān)日志信息記錄到文件以便分析. 與上面幾個(gè)配合使用 | ||
| -XX:+PrintClassHistogram | garbage collects before printing the histogram. | ||
| -XX:+PrintTLAB | 查看TLAB空間的使用情況 | ||
| XX:+PrintTenuringDistribution | 查看每次minor GC后新的存活周期的閾值 | Desired survivor size 1048576 bytes, new threshold 7 (max 15) new threshold 7即標(biāo)識新的存活周期的閾值為7。 |
JVM GC垃圾回收器參數(shù)設(shè)置
JVM給出了3種選擇:串行收集器、并行收集器、并發(fā)收集器。串行收集器只適用于小數(shù)據(jù)量的情況,所以生產(chǎn)環(huán)境的選擇主要是并行收集器和并發(fā)收集器。默認(rèn)情況下JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在啟動(dòng)時(shí)加入相應(yīng)參數(shù)。JDK5.0以后,JVM會(huì)根據(jù)當(dāng)前系統(tǒng)配置進(jìn)行智能判斷。
串行收集器
-XX:+UseSerialGC:設(shè)置串行收集器。
并行收集器(吞吐量優(yōu)先)
-XX:+UseParallelGC:設(shè)置為并行收集器。此配置僅對年輕代有效。即年輕代使用并行收集,而年老代仍使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的線程數(shù),即:同時(shí)有多少個(gè)線程一起進(jìn)行垃圾回收。此值建議配置與CPU數(shù)目相等。
-XX:+UseParallelOldGC:配置年老代垃圾收集方式為并行收集。JDK6.0開始支持對年老代并行收集。
-XX:MaxGCPauseMillis=100:設(shè)置每次年輕代垃圾回收的最長時(shí)間(單位毫秒)。如果無法滿足此時(shí)間,JVM會(huì)自動(dòng)調(diào)整年輕代大小,以滿足此時(shí)間。
-XX:+UseAdaptiveSizePolicy:設(shè)置此選項(xiàng)后,并行收集器會(huì)自動(dòng)調(diào)整年輕代Eden區(qū)大小和Survivor區(qū)大小的比例,以達(dá)成目標(biāo)系統(tǒng)規(guī)定的最低響應(yīng)時(shí)間或者收集頻率等指標(biāo)。此參數(shù)建議在使用并行收集器時(shí),一直打開。
并發(fā)收集器(響應(yīng)時(shí)間優(yōu)先)
并行收集器
-XX:+UseConcMarkSweepGC:即CMS收集,設(shè)置年老代為并發(fā)收集。CMS收集是JDK1.4后期版本開始引入的新GC算法。它的主要適合場景是對響應(yīng)時(shí)間的重要性需求大于對吞吐量的需求,能夠承受垃圾回收線程和應(yīng)用線程共享CPU資源,并且應(yīng)用中存在比較多的長生命周期對象。CMS收集的目標(biāo)是盡量減少應(yīng)用的暫停時(shí)間,減少Full GC發(fā)生的幾率,利用和應(yīng)用程序線程并發(fā)的垃圾回收線程來標(biāo)記清除年老代內(nèi)存。
-XX:+UseParNewGC:設(shè)置年輕代為并發(fā)收集??膳cCMS收集同時(shí)使用。JDK5.0以上,JVM會(huì)根據(jù)系統(tǒng)配置自行設(shè)置,所以無需再設(shè)置此參數(shù)。
-XX:CMSFullGCsBeforeCompaction=0:由于并發(fā)收集器不對內(nèi)存空間進(jìn)行壓縮和整理,所以運(yùn)行一段時(shí)間并行收集以后會(huì)產(chǎn)生內(nèi)存碎片,內(nèi)存使用效率降低。此參數(shù)設(shè)置運(yùn)行0次Full GC后對內(nèi)存空間進(jìn)行壓縮和整理,即每次Full GC后立刻開始壓縮和整理內(nèi)存。
-XX:+UseCMSCompactAtFullCollection:打開內(nèi)存空間的壓縮和整理,在Full GC后執(zhí)行??赡軙?huì)影響性能,但可以消除內(nèi)存碎片。
-XX:+CMSIncrementalMode:設(shè)置為增量收集模式。一般適用于單CPU情況。
-XX:CMSInitiatingOccupancyFraction=70:表示年老代內(nèi)存空間使用到70%時(shí)就開始執(zhí)行CMS收集,以確保年老代有足夠的空間接納來自年輕代的對象,避免Full GC的發(fā)生。
其它垃圾回收參數(shù)
-XX:+ScavengeBeforeFullGC:年輕代GC優(yōu)于Full GC執(zhí)行。
-XX:-DisableExplicitGC:不響應(yīng) System.gc() 代碼。
-XX:+UseThreadPriorities:啟用本地線程優(yōu)先級API。即使 java.lang.Thread.setPriority() 生效,不啟用則無效。
-XX:SoftRefLRUPolicyMSPerMB=0:軟引用對象在最后一次被訪問后能存活0毫秒(JVM默認(rèn)為1000毫秒)。
-XX:TargetSurvivorRatio=90:允許90%的Survivor區(qū)被占用(JVM默認(rèn)為50%)。提高對于Survivor區(qū)的使用率。
JVM參數(shù)優(yōu)先級
-Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio 3組參數(shù)都可以影響年輕代的大小,混合使用的情況下,優(yōu)先級是什么?
答案如下:
高優(yōu)先級:-XX:NewSize/-XX:MaxNewSize
中優(yōu)先級:-Xmn(默認(rèn)等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
低優(yōu)先級:-XX:NewRatio推薦使用-Xmn參數(shù),原因是這個(gè)參數(shù)簡潔,相當(dāng)于一次設(shè)定 NewSize/MaxNewSIze,而且兩者相等,適用于生產(chǎn)環(huán)境。-Xmn 配合 -Xms/-Xmx,即可將堆內(nèi)存布局完成。
-Xmn參數(shù)是在JDK 1.4 開始支持。
下面用一些小案例加深理解:
HelloGC是java代碼編譯后的一個(gè)class文件,代碼:
public?class?T01_HelloGC?{
????public?static?void?main(String[]?args)?{
????????for(int?i=0;?i<10000;?i++)?{
????????????byte[]?b?=?new?byte[1024?*?1024];
????????}
????}
}java -XX:+PrintCommandLineFlags HelloGC
[root@localhost?courage]#?java?-XX:+PrintCommandLineFlags?T01_HelloGC
-XX:InitialHeapSize=61780800?-XX:MaxHeapSize=988492800?-XX:+PrintCommandLineFlags?-XX
:+UseCompressedClassPointers?-XX:+UseCompressedOops?-XX:+UseParallelGC?
java?-Xmn10M?-Xms40M?-Xmx60M?-XX:+PrintCommandLineFlags?-XX:+PrintGC??HelloGC
PrintGCDetails?PrintGCTimeStamps?PrintGCCauses
結(jié)果:
-XX:InitialHeapSize=41943040?-XX:MaxHeapSize=62914560?-XX:MaxNewSize=10485760?-XX:NewSize=10485760?-XX:+PrintCommandLineFlags?-XX:+PrintGC?-XX:+UseCompressedClassPointers?-XX:+UseCompressedOops?
-XX:+UseParallelGC[GC?(Allocation?Failure)??7839K->392K(39936K),?0.0015452?secs]
[GC?(Allocation?Failure)??7720K->336K(39936K),?0.0005439?secs]
[GC?(Allocation?Failure)??7656K->336K(39936K),?0.0005749?secs]
[GC?(Allocation?Failure)??7659K->368K(39936K),?0.0005095?secs]
[GC?(Allocation?Failure)??7693K->336K(39936K),?0.0004385?secs]
[GC?(Allocation?Failure)??7662K->304K(40448K),?0.0028468?secs]
......
命令解釋:
java:表示使用java執(zhí)行器執(zhí)行
-Xmn10M :表示設(shè)置年輕代值為10M
-Xms40M :表示設(shè)置堆內(nèi)存的最小Heap值為40M
-Xmx60M :表示設(shè)置堆內(nèi)存的最大Heap值為60M
-XX:+PrintCommandLineFlags:打印顯式隱式參數(shù),就是結(jié)果前三行
-XX:+PrintGC : 打印垃圾回收有關(guān)信息
HelloGC :這是需要執(zhí)行的啟動(dòng)類
PrintGCDetails :打印GC詳細(xì)信息
PrintGCTimeStamps :打印GC時(shí)間戳
PrintGCCauses :打印GC產(chǎn)生的原因
結(jié)果解釋:

java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGC
表示使用CMS垃圾收集器,同時(shí)打印參數(shù)
打印結(jié)果:-XX:InitialHeapSize=61780800?
-XX:MaxHeapSize=988492800?
-XX:MaxNewSize=329252864?
-XX:MaxTenuringThreshold=6?
-XX:OldPLABSize=16?
-XX:+PrintCommandLineFlags?
-XX:+UseCompressedClassPointers?
-XX:+UseCompressedOops?
-XX:+UseConcMarkSweepGC?
-XX:+UseParNewGCjava -XX:+PrintFlagsInitial 默認(rèn)參數(shù)值
java -XX:+PrintFlagsFinal 最終參數(shù)值
java -XX:+PrintFlagsFinal | grep xxx 找到對應(yīng)的參數(shù)
java -XX:+PrintFlagsFinal -version |grep GC
JVM調(diào)優(yōu)流程
JVM調(diào)優(yōu),設(shè)計(jì)到三個(gè)大的方面,在服務(wù)器出現(xiàn)問題之前要先根據(jù)業(yè)務(wù)場景選擇合適的垃圾處理器,設(shè)置不同的虛擬機(jī)參數(shù),運(yùn)行中觀察GC日志,分析性能,分析問題定位問題,虛擬機(jī)排錯(cuò)等內(nèi)容,如果服務(wù)器掛掉了,要及時(shí)生成日志文件便于找到問題所在。
調(diào)優(yōu)前的基礎(chǔ)概念
目前的垃圾處理器中,一類是以吞吐量優(yōu)先,一類是以響應(yīng)時(shí)間優(yōu)先:
吞吐量=用戶代碼執(zhí)行時(shí)間用戶代碼執(zhí)行時(shí)間+垃圾回收執(zhí)行時(shí)間
響應(yīng)時(shí)間:STW越短,響應(yīng)時(shí)間越好
對吞吐量、響應(yīng)時(shí)間、QPS、并發(fā)數(shù)相關(guān)概念可以參考:吞吐量(TPS)、QPS、并發(fā)數(shù)、響應(yīng)時(shí)間(RT)概念
所謂調(diào)優(yōu),首先確定追求什么,是吞吐量? 還是追求響應(yīng)時(shí)間?還是在滿足一定的響應(yīng)時(shí)間的情況下,要求達(dá)到多大的吞吐量,等等。一般情況下追求吞吐量的有以下領(lǐng)域:科學(xué)計(jì)算、數(shù)據(jù)挖掘等。吞吐量優(yōu)先的垃圾處理器組合一般為:Parallel Scavenge + Parallel Old (PS + PO)。
而追求響應(yīng)時(shí)間的業(yè)務(wù)有:網(wǎng)站相關(guān) (JDK 1.8之后 G1,之前可以ParNew + CMS + Serial Old)
什么是調(diào)優(yōu)?
根據(jù)需求進(jìn)行JVM規(guī)劃和預(yù)調(diào)優(yōu)
優(yōu)化運(yùn)行JVM運(yùn)行環(huán)境(慢,卡頓)
解決JVM運(yùn)行過程中出現(xiàn)的各種問題(OOM)
調(diào)優(yōu)之前的規(guī)劃
調(diào)優(yōu),從業(yè)務(wù)場景開始,沒有業(yè)務(wù)場景的調(diào)優(yōu)都是耍流氓
無監(jiān)控(壓力測試,能看到結(jié)果),不調(diào)優(yōu)
步驟:
日志參數(shù)解釋說明:
/opt/xxx/logs/xxx-xxx-gc-%t.log 中XXX表示路徑,%t表示時(shí)間戳,意思是給日志文件添加一個(gè)時(shí)間標(biāo)記,如果不添加的話,也就意味著每次虛擬機(jī)啟動(dòng)都會(huì)使用原來的日志名,那么會(huì)被重寫。
Rotation中文意思是循環(huán)、輪流,意味著這個(gè)GC日志會(huì)循環(huán)寫
GCLogFileSize=20M 指定一個(gè)日志大小為20M,太大了不利于分析,太小又會(huì)產(chǎn)生過多的日志文件
NumberOfGCLogFiles=5 : 指定生成的日志數(shù)目
PrintGCDateStamps :PrintGCDateStamps會(huì)打印具體的時(shí)間,而PrintGCTimeStamps
主要打印針對JVM啟動(dòng)的時(shí)候的相對時(shí)間,相對來說前者更消耗內(nèi)存。
或者每天產(chǎn)生一個(gè)日志文件
響應(yīng)時(shí)間、停頓時(shí)間 [CMS G1 ZGC] (需要給用戶作響應(yīng))
吞吐量 = 用戶時(shí)間 /( 用戶時(shí)間 + GC時(shí)間) [PS+PO]
熟悉業(yè)務(wù)場景(沒有最好的垃圾回收器,只有最合適的垃圾回收器)
選擇回收器組合
計(jì)算內(nèi)存需求(經(jīng)驗(yàn)值 1.5G 16G)
選定CPU(越高越好)
設(shè)定年代大小、升級年齡
設(shè)定日志參數(shù)
觀察日志情況
日志有分析工具,可視化分析工具有GCeasy和GCViewer。
CPU高負(fù)荷排查流程
系統(tǒng)CPU經(jīng)常100%,如何調(diào)優(yōu)?(面試高頻) CPU100%那么一定有線程在占用系統(tǒng)資源,
找出哪個(gè)進(jìn)程cpu高(top)
該進(jìn)程中的哪個(gè)線程cpu高(top -Hp)
導(dǎo)出該線程的堆棧 (jstack)
查找哪個(gè)方法(棧幀)消耗時(shí)間 (jstack)
工作線程占比高 | 垃圾回收線程占比高
系統(tǒng)內(nèi)存飆高,如何查找問題?(面試高頻)
導(dǎo)出堆內(nèi)存 (jmap)
分析 (jhat jvisualvm mat jprofiler ... )
如何監(jiān)控JVM
jstat jvisualvm jprofiler arthas top...
CPU高負(fù)荷排查案例
測試代碼:
import?java.math.BigDecimal;
import?java.util.ArrayList;
import?java.util.Date;
import?java.util.List;
import?java.util.concurrent.ScheduledThreadPoolExecutor;
import?java.util.concurrent.ThreadPoolExecutor;
import?java.util.concurrent.TimeUnit;
/**
?*?從數(shù)據(jù)庫中讀取信用數(shù)據(jù),套用模型,并把結(jié)果進(jìn)行記錄和傳輸
?*/
public?class?T15_FullGC_Problem01?{
????private?static?class?CardInfo?{
????????BigDecimal?price?=?new?BigDecimal(0.0);
????????String?name?=?"張三";
????????int?age?=?5;
????????Date?birthdate?=?new?Date();
????????public?void?m()?{}
????}
????private?static?ScheduledThreadPoolExecutor?executor?=?new?ScheduledThreadPoolExecutor(50,
????????????new?ThreadPoolExecutor.DiscardOldestPolicy());
????public?static?void?main(String[]?args)?throws?Exception?{
????????executor.setMaximumPoolSize(50);
????????for?(;;){
????????????modelFit();
????????????Thread.sleep(100);
????????}
????}
????private?static?void?modelFit(){
????????List?taskList?=?getAllCardInfo();
????????taskList.forEach(info?->?{
????????????//?do?something
????????????executor.scheduleWithFixedDelay(()?->?{
????????????????//do?sth?with?info
????????????????info.m();
????????????},?2,?3,?TimeUnit.SECONDS);
????????});
????}
????private?static?List?getAllCardInfo(){
????????List?taskList?=?new?ArrayList<>();
????????for?(int?i?=?0;?i?100;?i++)?{
????????????CardInfo?ci?=?new?CardInfo();
????????????taskList.add(ci);
????????}
????????return?taskList;
????}
}java -Xms200M -Xmx200M -XX:+PrintGC com.courage.jvm.gc.T15_FullGC_Problem01
收到CPU報(bào)警信息(CPU Memory)
top命令觀察到問題:內(nèi)存不斷增長 CPU占用率居高不下
[root@localhost?~]#?top
top?-?22:03:18?up?40?min,??5?users,??load?average:?0.09,?0.16,?0.34
Tasks:?210?total,???1?running,?209?sleeping,???0?stopped,???0?zombie
%Cpu(s):??0.2?us,??3.0?sy,??0.0?ni,?96.8?id,??0.0?wa,??0.0?hi,??0.0?si,??0.0?st
KiB?Mem?:??3861300?total,??2355260?free,???904588?used,???601452?buff/cache
KiB?Swap:??4063228?total,??4063228?free,????????0?used.??2716336?avail?Mem?
???PID?USER??????PR??NI????VIRT????RES????SHR?S??%CPU?%MEM?????TIME+?COMMAND?????????????????
??3751?root??????20???0?3780976??93864??11816?S??42.2??2.4???0:21.00?java
??1868?mysql?????20???0?1907600?357452??14744?S???0.7??9.3???0:17.40?mysqld
??3816?root??????20???0??162124???2352???1580?R???0.3??0.1???0:00.12?toptop -Hp 觀察進(jìn)程中的線程,哪個(gè)線程CPU和內(nèi)存占比高
[root@localhost?~]#?top?-Hp?3751
top?-?22:03:15?up?40?min,??5?users,??load?average:?0.09,?0.16,?0.34
Threads:??66?total,???0?running,??66?sleeping,???0?stopped,???0?zombie
%Cpu(s):??0.0?us,??2.5?sy,??0.0?ni,?97.5?id,??0.0?wa,??0.0?hi,??0.0?si,??0.0?st
KiB?Mem?:??3861300?total,??2354800?free,???905048?used,???601452?buff/cache
KiB?Swap:??4063228?total,??4063228?free,????????0?used.??2715876?avail?Mem?
???PID?USER??????PR??NI????VIRT????RES????SHR?S?%CPU?%MEM?????TIME+?COMMAND??????????????
??3801?root??????20???0?3780976??93864??11816?S??1.3??2.4???0:00.40?java
??3766?root??????20???0?3780976??93864??11816?S??1.0??2.4???0:00.37?java
??3768?root??????20???0?3780976??93864??11816?S??1.0??2.4???0:00.36?java
??3770?root??????20???0?3780976??93864??11816?S??1.0??2.4???0:00.39?javajps定位具體java進(jìn)程,jstack 定位線程狀況
需要注意的是,
jstack與top -Hp Port導(dǎo)出的棧端口號存在十六進(jìn)制轉(zhuǎn)換關(guān)系,例如jstack導(dǎo)出的" nid=0xf10 "對應(yīng)"3801"。
對于上面打印的信息,重點(diǎn)關(guān)注跟Waiting有關(guān)的,看看在等待什么,例如:假如有一個(gè)進(jìn)程中100個(gè)線程,很多線程都在waiting on ,一定要找到是哪個(gè)線程持有這把鎖,怎么找?搜索jstack dump的信息,看哪個(gè)線程持有這把鎖RUNNABLE。
如果僅僅是看JAVA線程,可以使用
jps命令重點(diǎn)關(guān)注:為什么阿里規(guī)范里規(guī)定,線程的名稱(尤其是線程池)都要寫有意義的名稱 怎么樣自定義線程池里的線程名稱?(自定義ThreadFactory)

jinfo pid 進(jìn)程詳細(xì)信息
[root@localhost?~]#?jinfo?6741
Attaching?to?process?ID?6741,?please?wait...
Debugger?attached?successfully.
Server?compiler?detected.
JVM?version?is?25.271-b09
Java?System?Properties:
java.runtime.name?=?Java(TM)?SE?Runtime?Environment
java.vm.version?=?25.271-b09
sun.boot.library.path?=?/usr/local/java/jdk1.8.0_271/jre/lib/amd64
java.vendor.url?=?http://java.oracle.com/
java.vm.vendor?=?Oracle?Corporation
path.separator?=?:
file.encoding.pkg?=?sun.io
java.vm.name?=?Java?HotSpot(TM)?64-Bit?Server?VM
sun.os.patch.level?=?unknown
sun.java.launcher?=?SUN_STANDARD
user.country?=?CN
user.dir?=?/usr/courage/gc/com/courage
java.vm.specification.name?=?Java?Virtual?Machine?Specification
java.runtime.version?=?1.8.0_271-b09
java.awt.graphicsenv?=?sun.awt.X11GraphicsEnvironment
os.arch?=?amd64
java.endorsed.dirs?=?/usr/local/java/jdk1.8.0_271/jre/lib/endorsed
java.io.tmpdir?=?/tmp
line.separator?=?
java.vm.specification.vendor?=?Oracle?Corporation
os.name?=?Linux
sun.jnu.encoding?=?UTF-8
java.library.path?=?/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/
libjava.specification.name?=?Java?Platform?API?Specification
java.class.version?=?52.0
sun.management.compiler?=?HotSpot?64-Bit?Tiered?Compilers
os.version?=?3.10.0-1127.el7.x86_64
user.home?=?/root
user.timezone?=?
java.awt.printerjob?=?sun.print.PSPrinterJob
file.encoding?=?UTF-8
java.specification.version?=?1.8
user.name?=?root
java.class.path?=?.
java.vm.specification.version?=?1.8
sun.arch.data.model?=?64
sun.java.command?=?T15_FullGC_Problem01
java.home?=?/usr/local/java/jdk1.8.0_271/jre
user.language?=?zh
java.specification.vendor?=?Oracle?Corporation
awt.toolkit?=?sun.awt.X11.XToolkit
java.vm.info?=?mixed?mode
java.version?=?1.8.0_271
java.ext.dirs?=?/usr/local/java/jdk1.8.0_271/jre/lib/ext:/usr/java/packages/l
ib/extsun.boot.class.path?=?/usr/local/java/jdk1.8.0_271/jre/lib/resources.jar:/usr
/local/java/jdk1.8.0_271/jre/lib/rt.jar:/usr/local/java/jdk1.8.0_271/jre/lib/sunrsasign.jar:/usr/local/java/jdk1.8.0_271/jre/lib/jsse.jar:/usr/local/java/jdk1.8.0_271/jre/lib/jce.jar:/usr/local/java/jdk1.8.0_271/jre/lib/charsets.jar:/usr/local/java/jdk1.8.0_271/jre/lib/jfr.jar:/usr/local/java/jdk1.8.0_271/jre/classesjava.vendor?=?Oracle?Corporation
file.separator?=?/
java.vendor.url.bug?=?http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding?=?UnicodeLittle
sun.cpu.endian?=?little
sun.cpu.isalist?=?
VM?Flags:
Non-default?VM?flags:?-XX:CICompilerCount=3?-XX:InitialHeapSize=209715200?-XX
:MaxHeapSize=209715200?-XX:MaxNewSize=69730304?-XX:MinHeapDeltaBytes=524288?-XX:NewSize=69730304?-XX:OldSize=139984896?-XX:+PrintGC?-XX:+UseCompressedClassPointers?-XX:+UseCompressedOops?-XX:+UseFastUnorderedTimeStamps?-XX:+UseParallelGC?Command?line:??-Xms200M?-Xmx200M?-XX:+PrintGCjstat -gc 動(dòng)態(tài)觀察gc情況 / 閱讀GC日志發(fā)現(xiàn)頻繁GC / arthas觀察 / jconsole/jvisualVM/ Jprofiler(最好用)
jstat gc 4655 500 : 每500毫秒打印端口4655的GC的情況

S0C:第一個(gè)幸存區(qū)的大小
S1C:第二個(gè)幸存區(qū)的大小
S0U:第一個(gè)幸存區(qū)的使用大小
S1U:第二個(gè)幸存區(qū)的使用大小
EC:伊甸園區(qū)的大小
EU:伊甸園區(qū)的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法區(qū)大小
MU:方法區(qū)使用大小
CCSC:壓縮類空間大小
CCSU:壓縮類空間使用大小
YGC:年輕代垃圾回收次數(shù)
YGCT:年輕代垃圾回收消耗時(shí)間
FGC:老年代垃圾回收次數(shù)
FGCT:老年代垃圾回收消耗時(shí)間
GCT:垃圾回收消耗總時(shí)間
如果面試官問你是怎么定位OOM問題的?能否用圖形界面(不能!因?yàn)閳D形界面會(huì)影響服務(wù)器性能)
1:已經(jīng)上線的系統(tǒng)不用圖形界面用什么?(cmdline arthas)
2:圖形界面到底用在什么地方?測試!測試的時(shí)候進(jìn)行監(jiān)控?。▔簻y觀察)
jmap -histo 6892 | head -10,查找有多少對象產(chǎn)生

jmap -dump:format=b,file=xxx pid :
線上系統(tǒng),內(nèi)存特別大,jmap執(zhí)行期間會(huì)對進(jìn)程產(chǎn)生很大影響,甚至卡頓(電商不適合)
1:設(shè)定了參數(shù)HeapDump,OOM的時(shí)候會(huì)自動(dòng)產(chǎn)生堆轉(zhuǎn)儲文件
2:很多服務(wù)器備份(高可用),停掉這臺服務(wù)器對其他服務(wù)器不影響
3:在線定位(一般小點(diǎn)兒公司用不到)dump文件存放位置:

java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.courage.jvm.gc.T15_FullGC_Problem01
上面的意思是當(dāng)發(fā)生內(nèi)存溢出時(shí)自動(dòng)生成堆轉(zhuǎn)儲文件,需要注意的是,如果生成了這個(gè)文件先不要重啟服務(wù)器,將這個(gè)文件保存好之后再重啟。使用MAT / jhat /jvisualvm 進(jìn)行dump文件分析
報(bào)錯(cuò):

原因是設(shè)置的堆最大值太小了,將512M設(shè)置成1024M重新啟動(dòng)即可:
```shell
[root@localhost?~]#?jhat?-J-Xmx1024M?2021_2_8.dump
Reading?from?2021_2_8.dump...
Dump?file?created?Mon?Feb?08?09:00:56?CST?2021
Snapshot?read,?resolving...
Resolving?4609885?objects...
Chasing?references,?expect?921?dots..........................................................
.........................................................................................Eliminating?duplicate?references.............................................................
......................................................................................Snapshot?resolved.
Started?HTTP?server?on?port?7000
Server?is?ready.
瀏覽器輸入請求http://192.168.182.130:7000 即可查看,拉到最后:找到對應(yīng)鏈接 可以使用OQL查找特定問題對象

其他可以參考:白灰——軟件測試
最后找到代碼的問題
JVM調(diào)優(yōu)工具
jconsole遠(yuǎn)程連接
程序啟動(dòng)加入?yún)?shù):
如果遭遇 Local host name unknown:XXX的錯(cuò)誤,修改/etc/hosts文件,把XXX加入進(jìn)去
關(guān)閉linux防火墻(實(shí)戰(zhàn)中應(yīng)該打開對應(yīng)端口)
windows上打開 jconsole遠(yuǎn)程連接 192.168.182.130:11111
jvisualvm遠(yuǎn)程連接
這個(gè)軟件在JDK8以后版本中移除了,使用的話需要額外下載,并且要在etc/visualvm.conf中修改默認(rèn)的JDK_Home地址。
參考:使用jvisualvm的jstatd方式遠(yuǎn)程監(jiān)控Java程序
阿里巴巴Arthas
這個(gè)直接看官網(wǎng)就行了,純中文:Arthas 用戶文檔
JVM調(diào)優(yōu)案例
參數(shù)設(shè)置之承受海量訪問的動(dòng)態(tài)Web應(yīng)用
服務(wù)器配置:8 核 CPU, 8G MEM, JDK 1.6.X
參數(shù)方案:
-server -Xmx3550m -Xms3550m -Xmn1256m -Xss128k -XX:SurvivorRatio=6 -XX:MaxPermSize=256m -XX:ParallelGCThreads=8 -XX:MaxTenuringThreshold=0 -XX:+UseConcMarkSweepGC
調(diào)優(yōu)說明:
-Xmx 與 -Xms 相同以避免JVM反復(fù)重新申請內(nèi)存。-Xmx 的大小約等于系統(tǒng)內(nèi)存大小的一半,即充分利用系統(tǒng)資源,又給予系統(tǒng)安全運(yùn)行的空間。
-Xmn1256m 設(shè)置年輕代大小為1256MB。此值對系統(tǒng)性能影響較大,Sun官方推薦配置年輕代大小為整個(gè)堆的3/8。
-Xss128k 設(shè)置較小的線程棧以支持創(chuàng)建更多的線程,支持海量訪問,并提升系統(tǒng)性能。
-XX:SurvivorRatio=6 設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的比值。系統(tǒng)默認(rèn)是8,根據(jù)經(jīng)驗(yàn)設(shè)置為6,則2個(gè)Survivor區(qū)與1個(gè)Eden區(qū)的比值為2:6,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/8。
-XX:ParallelGCThreads=8 配置并行收集器的線程數(shù),即同時(shí)8個(gè)線程一起進(jìn)行垃圾回收。此值一般配置為與CPU數(shù)目相等。
-XX:MaxTenuringThreshold=0 設(shè)置垃圾最大年齡(在年輕代的存活次數(shù))。如果設(shè)置為0的話,則年輕代對象不經(jīng)過Survivor區(qū)直接進(jìn)入年老代。對于年老代比較多的應(yīng)用,可以提高效率;如果將此值設(shè)置為一個(gè)較大值,則年輕代對象會(huì)在Survivor區(qū)進(jìn)行多次復(fù)制,這樣可以增加對象再年輕代的存活時(shí)間,增加在年輕代即被回收的概率。根據(jù)被海量訪問的動(dòng)態(tài)Web應(yīng)用之特點(diǎn),其內(nèi)存要么被緩存起來以減少直接訪問DB,要么被快速回收以支持高并發(fā)海量請求,因此其內(nèi)存對象在年輕代存活多次意義不大,可以直接進(jìn)入年老代,根據(jù)實(shí)際應(yīng)用效果,在這里設(shè)置此值為0。
-XX:+UseConcMarkSweepGC 設(shè)置年老代為并發(fā)收集。CMS(ConcMarkSweepGC)收集的目標(biāo)是盡量減少應(yīng)用的暫停時(shí)間,減少Full GC發(fā)生的幾率,利用和應(yīng)用程序線程并發(fā)的垃圾回收線程來標(biāo)記清除年老代內(nèi)存,適用于應(yīng)用中存在比較多的長生命周期對象的情況。
參數(shù)設(shè)置之內(nèi)部集成構(gòu)建服務(wù)器
高性能數(shù)據(jù)處理的工具應(yīng)用
服務(wù)器配置:1 核 CPU, 4G MEM, JDK 1.6.X
參數(shù)方案:
-server -XX:PermSize=196m -XX:MaxPermSize=196m -Xmn320m -Xms768m -Xmx1024m
調(diào)優(yōu)說明:
-XX:PermSize=196m -XX:MaxPermSize=196m 根據(jù)集成構(gòu)建的特點(diǎn),大規(guī)模的系統(tǒng)編譯可能需要加載大量的Java類到內(nèi)存中,所以預(yù)先分配好大量的持久代內(nèi)存是高效和必要的。
-Xmn320m 遵循年輕代大小為整個(gè)堆的3/8原則。
-Xms768m -Xmx1024m 根據(jù)系統(tǒng)大致能夠承受的堆內(nèi)存大小設(shè)置即可。
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長按上方微信二維碼?2 秒
感謝點(diǎn)贊支持下哈?
