主頁 > 軟體設計 > 【JVM進階之路】十:JVM調優總結

【JVM進階之路】十:JVM調優總結

2021-04-12 10:35:40 軟體設計

1、調優原則

JVM調優聽起來很高大上,但是要認識到,JVM調優應該是Java性能優化的最后一顆子彈,

Java專案需要調優嗎

比較認可廖雪峰老師的觀點,要認識到JVM調優不是常規手段,性能問題一般第一選擇是優化程式,最后的選擇才是進行JVM調優,

調優層級

JVM的自動記憶體管理本來就是為了將開發人員從記憶體管理的泥潭里拉出來,即使不得不進行JVM調優,也絕對不能拍腦門就去調整引數,一定要全面監控,詳細分析性能資料,

2、JVM調優的時機

不得不考慮進行JVM調優的是那些情況呢?

  • Heap記憶體(老年代)持續上漲達到設定的最大記憶體值;
  • Full GC 次數頻繁;
  • GC 停頓時間過長(超過1秒);
  • 應用出現OutOfMemory 等記憶體例外;
  • 應用中有使用本地快取且占用大量記憶體空間;
  • 系統吞吐量與回應性能不高或下降,

3、JVM調優的目標

吞吐量、延遲、記憶體占用三者類似CAP,構成了一個不可能三角,只能選擇其中兩個進行調優,不可三者兼得,

  • 延遲:GC低停頓和GC低頻率;
  • 低記憶體占用;
  • 高吞吐量;

選擇了其中兩個,必然會會以犧牲另一個為代價,

下面展示了一些JVM調優的量化目標參考實體:

  • Heap 記憶體使用率 <= 70%;
  • Old generation記憶體使用率<= 70%;
  • avgpause <= 1秒;
  • Full gc 次數0 或 avg pause interval >= 24小時 ;

注意:不同應用的JVM調優量化目標是不一樣的,

4、JVM調優的步驟

一般情況下,JVM調優可通過以下步驟進行:

  • 分析系統系統運行情況:分析GC日志及dump檔案,判斷是否需要優化,確定瓶頸問題點;
  • 確定JVM調優量化目標;
  • 確定JVM調優引數(根據歷史JVM引數來調整);
  • 依次確定調優記憶體、延遲、吞吐量等指標;
  • 對比觀察調優前后的差異;
  • 不斷的分析和調整,直到找到合適的JVM引數配置;
  • 找到最合適的引數,將這些引數應用到所有服務器,并進行后續跟蹤,

以上操作步驟中,某些步驟是需要多次不斷迭代完成的,一般是從滿足程式的記憶體使用需求開始的,之后是時間延遲的要求,最后才是吞吐量的要求,要基于這個步驟來不斷優化,每一個步驟都是進行下一步的基礎,不可逆行,

JVM調優步驟

5、JVM引數

下面來看一下JDK的JVM引數,

5.1、基本引數

引數名稱含義默認值
-Xms初始堆大小記憶體的1/64默認(MinHeapFreeRatio引數可以調整)空余堆記憶體小于40%時,JVM就會增大堆直到-Xmx的最大限制.
-Xmx最大堆大小記憶體的1/4默認(MaxHeapFreeRatio引數可以調整)空余堆記憶體大于70%時,JVM會減少堆直到 -Xms的最小限制
-Xmn年輕代大小注意:此處的大小是(eden+ 2 survivor space).與jmap -heap中顯示的New gen是不同的, 整個堆大小=年輕代大小 + 年老代大小 + 持久代大小. 增大年輕代后,將會減小年老代大小.此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8
-XX:NewSize設定年輕代大小
-XX:MaxNewSize年輕代最大值
-XX:PermSize設定持久代(perm gen)初始值記憶體的1/64JDK1.8以前
-XX:MaxPermSize設定持久代最大值記憶體的1/4JDK1.8以前
-Xss每個執行緒的堆疊大小JDK5.0以后每個執行緒堆疊大小為1M,以前每個執行緒堆疊大小為256K.更具應用的執行緒所需記憶體大小進行 調整.在相同物理記憶體下,減小這個值能生成更多的執行緒.但是作業系統對一個行程內的執行緒數還是有限制的,不能無限生成,經驗值在3000~5000左右 一般小的應用, 如果堆疊不是很深, 應該是128k夠用的 大的應用建議使用256k,這個選項對性能影響比較大,需要嚴格的測驗,(校長) 和threadstacksize選項解釋很類似,官方檔案似乎沒有解釋,在論壇中有這樣一句話:"” -Xss is translated in a VM flag named ThreadStackSize” 一般設定這個值就可以了,
-XX:ThreadStackSizeThread 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和兩個Survivor區)與年老代的比值(除去持久代)-XX:NewRatio=4表示年輕代與年老代所占比值為1:4,年輕代占整個堆疊的1/5 Xms=Xmx并且設定了Xmn的情況下,該引數不需要進行設定,
-XX:SurvivorRatioEden區與Survivor區的大小比值設定為8,則兩個Survivor區與一個Eden區的比值為2:8,一個Survivor區占整個年輕代的1/10
-XX:LargePageSizeInBytes記憶體頁的大小不可設定過大, 會影響Perm的大小=128m
-XX:+UseFastAccessorMethods原始型別的快速優化
-XX:+DisableExplicitGC關閉System.gc()這個引數需要嚴格的測驗
-XX:+ExplicitGCInvokesConcurrent關閉System.gc()disabledEnables invoking of concurrent GC by using the System.gc() request. This option is disabled by default and can be enabled only together with the -XX:+UseConcMarkSweepGC option.
-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses關閉System.gc()disabledEnables invoking of concurrent GC by using the System.gc() request and unloading of classes during the concurrent GC cycle. This option is disabled by default and can be enabled only together with the -XX:+UseConcMarkSweepGC option.
-XX:MaxTenuringThreshold垃圾最大年齡如果設定為0的話,則年輕代物件不經過Survivor區,直接進入年老代. 對于年老代比較多的應用,可以提高效率.如果將此值設定為一個較大值,則年輕代物件會在Survivor區進行多次復制,這樣可以增加物件再年輕代的存活 時間,增加在年輕代即被回收的概率 該引數只有在串行GC時才有效.
-XX:+AggressiveOpts加快編譯
-XX:+UseBiasedLocking鎖機制的性能改善
-Xnoclassgc禁用垃圾回收
-XX:SoftRefLRUPolicyMSPerMB每兆堆空閑空間中SoftReference的存活時間1ssoftly 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單位位元組 新生代采用Parallel Scavenge GC時無效 另一種直接在舊生代分配的情況是大的陣列物件,且陣列中無外部參考物件.
-XX:TLABWasteTargetPercentTLAB占eden區的百分比1%
-XX:+CollectGen0FirstFullGC時是否先YGCfalse

Jdk7版本的主要引數

引數名稱含義默認值
-XX:PermSize設定持久代Jdk7版本及以前版本
-XX:MaxPermSize設定最大持久代Jdk7版本及以前版本

Jdk8版本的重要特有引數

引數名稱含義默認值
-XX:MetaspaceSize元空間大小Jdk8版本
-XX:MaxMetaspaceSize最大元空間Jdk8版本

5.2、并行收集器相關引數

引數名稱含義默認值
-XX:+UseParallelGCFull GC采用parallel MSC (此項待驗證)選擇垃圾收集器為并行收集器.此配置僅對年輕代有效.即上述配置下,年輕代使用并發收集,而年老代仍舊使用串行收集.(此項待驗證)
-XX:+UseParNewGC設定年輕代為并行收集可與CMS收集同時使用 JDK5.0以上,JVM會根據系統配置自行設定,所以無需再設定此值
-XX:ParallelGCThreads并行收集器的執行緒數此值最好配置與處理器數目相等 同樣適用于CMS
-XX:+UseParallelOldGC年老代垃圾收集方式為并行收集(Parallel Compacting)這個是JAVA 6出現的引數選項
-XX:MaxGCPauseMillis每次年輕代垃圾回收的最長時間(最大暫停時間)如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此值.
-XX:+UseAdaptiveSizePolicy自動選擇年輕代區大小和相應的Survivor區比例設定此選項后,并行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目標系統規定的最低相應時間或者收集頻率等,此值建議使用并行收集器時,一直打開.
-XX:GCTimeRatio設定垃圾回收時間占程式運行時間的百分比公式為1/(1+n)
-XX:+ScavengeBeforeFullGCFull GC前呼叫YGCtrueDo young generation GC prior to a full GC. (Introduced in 1.4.1.)

5.3、CMS相關引數

引數名稱含義默認值
-XX:+UseConcMarkSweepGC使用CMS記憶體收集測驗中配置這個以后,-XX:NewRatio=4的配置失效了,原因不明.所以,此時年輕代大小最好用-Xmn設定.???
-XX:+AggressiveHeap試圖是使用大量的物理記憶體 長時間大記憶體使用的優化,能檢查計算資源(記憶體, 處理器數量) 至少需要256MB記憶體 大量的CPU/記憶體, (在1.4.1在4CPU的機器上已經顯示有提升)
-XX:CMSFullGCsBeforeCompaction多少次后進行記憶體壓縮由于并發收集器不對記憶體空間進行壓縮,整理,所以運行一段時間以后會產生"碎片",使得運行效率降低.此值設定運行多少次GC以后對記憶體空間進行壓縮,整理.
-XX:+CMSParallelRemarkEnabled降低標記停頓
-XX+UseCMSCompactAtFullCollection在FULL GC的時候, 對年老代的壓縮CMS是不會移動記憶體的, 因此, 這個非常容易產生碎片, 導致記憶體不夠用, 因此, 記憶體的壓縮這個時候就會被啟用, 增加這個引數是個好習慣, 可能會影響性能,但是可以消除碎片
-XX:+UseCMSInitiatingOccupancyOnly使用手動定義初始化定義開始CMS收集禁止hostspot自行觸發CMS GC
-XX:CMSInitiatingOccupancyFraction=70使用cms作為垃圾回收 使用70%后開始CMS收集92為了保證不出現promotion failed(見下面介紹)錯誤,該值的設定需要滿足以下公式CMSInitiatingOccupancyFraction計算公式
-XX:CMSInitiatingPermOccupancyFraction設定Perm Gen使用到達多少比率時觸發92
-XX:+CMSIncrementalMode設定為增量模式用于單CPU情況
-XX:+CMSClassUnloadingEnabled

5.4、輔助資訊

引數名稱含義默認值
-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列印垃圾回收期間程式暫停的時間.可與上面混合使用輸出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:+PrintGCApplicationConcurrentTime列印每次垃圾回收前,程式未中斷的執行時間.可與上面混合使用輸出形式:Application time: 0.5291524 seconds
-XX:+PrintHeapAtGC列印GC前后的詳細堆疊資訊
-Xloggc:filename把相關日志資訊記錄到檔案以便分析. 與上面幾個配合使用
-XX:+PrintClassHistogramgarbage 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即標識新的存活周期的閾值為7,

6、主要工具

6.1、JDK工具

JDK自帶了很多性能監控工具,我們可以用這些工具來監測系統和排查記憶體性能問題,

JDK自帶工具

6.2、Linux 命令列工具

進行性能監控和問題排查的時候,常常是結合作業系統本身的命令列工具來進行,

命令說明
top實時顯示正在執行行程的 CPU 使用率、記憶體使用率以及系統負載等資訊
vmstat對作業系統的虛擬記憶體、行程、CPU活動進行監控
pidstat監控指定行程的背景關系切換
iostat監控磁盤IO

其它還有一些第三方的監控工具,同樣是性能分析和故障排查的利器,如MATGChistoJProfilerarthas

7、常用調優策略

這里還是要提一下,及時確定要進行JVM調優,也不要陷入“知見障”,進行分析之后,發現可以通過優化程式提升性能,仍然首選優化程式,

7.1、選擇合適的垃圾回收器

CPU單核,那么毫無疑問Serial 垃圾收集器是你唯一的選擇,

CPU多核,關注吞吐量 ,那么選擇PS+PO組合,

CPU多核,關注用戶停頓時間,JDK版本1.6或者1.7,那么選擇CMS,

CPU多核,關注用戶停頓時間,JDK1.8及以上,JVM可用記憶體6G以上,那么選擇G1,

引數配置:

 //設定Serial垃圾收集器(新生代)
 開啟:-XX:+UseSerialGC
 ?
 //設定PS+PO,新生代使用功能Parallel Scavenge 老年代將會使用Parallel Old收集器
 開啟 -XX:+UseParallelOldGC
 ?
 //CMS垃圾收集器(老年代)
 開啟 -XX:+UseConcMarkSweepGC
 ?
 //設定G1垃圾收集器
 開啟 -XX:+UseG1GC

7.2、調整記憶體大小

現象:垃圾收集頻率非常頻繁,

原因:如果記憶體太小,就會導致頻繁的需要進行垃圾收集才能釋放出足夠的空間來創建新的物件,所以增加堆記憶體大小的效果是非常顯而易見的,

注意:如果垃圾收集次數非常頻繁,但是每次能回收的物件非常少,那么這個時候并非記憶體太小,而可能是記憶體泄露導致物件無法回收,從而造成頻繁GC,

引數配置:

 //設定堆初始值
 指令1-Xms2g
 指令2-XX:InitialHeapSize=2048m
 ?
 //設定堆區最大值
 指令1:`-Xmx2g` 
 指令2-XX:MaxHeapSize=2048m
 ?
 //新生代記憶體配置
 指令1-Xmn512m
 指令2-XX:MaxNewSize=512m

7.3、設定符合預期的停頓時間

現象:程式間接性的卡頓

原因:如果沒有確切的停頓時間設定,垃圾收集器以吞吐量為主,那么垃圾收集時間就會不穩定,

注意:不要設定不切實際的停頓時間,單次時間越短也意味著需要更多的GC次數才能回收完原有數量的垃圾.

引數配置:

 //GC停頓時間,垃圾收集器會嘗試用各種手段達到這個時間
 -XX:MaxGCPauseMillis 

7.4、調整記憶體區域大小比率

現象:某一個區域的GC頻繁,其他都正常,

原因:如果對應區域空間不足,導致需要頻繁GC來釋放空間,在JVM堆記憶體無法增加的情況下,可以調整對應區域的大小比率,

注意:也許并非空間不足,而是因為記憶體泄造成記憶體無法回收,從而導致GC頻繁,

引數配置:

 //survivor區和Eden區大小比率
 指令:-XX:SurvivorRatio=6  //S區和Eden區占新生代比率為1:6,兩個S區2:6
 ?
 //新生代和老年代的占比
 -XX:NewRatio=4  //表示新生代:老年代 = 1:4 即老年代占整個堆的4/5;默認值=2

7.5、調整物件升老年代的年齡

現象:老年代頻繁GC,每次回收的物件很多,

原因:如果升代年齡小,新生代的物件很快就進入老年代了,導致老年代物件變多,而這些物件其實在隨后的很短時間內就可以回收,這時候可以調整物件的升級代年齡,讓物件不那么容易進入老年代解決老年代空間不足頻繁GC問題,

注意:增加了年齡之后,這些物件在新生代的時間會變長可能導致新生代的GC頻率增加,并且頻繁復制這些物件新生的GC時間也可能變長,

配置引數:

//進入老年代最小的GC年齡,年輕代物件轉換為老年代物件最小年齡值,默認值7
 -XX:InitialTenuringThreshol=7 

7.6、調整大物件的標準

現象:老年代頻繁GC,每次回收的物件很多,而且單個物件的體積都比較大,

原因:如果大量的大物件直接分配到老年代,導致老年代容易被填滿而造成頻繁GC,可設定物件直接進入老年代的標準,

注意:這些大物件進入新生代后可能會使新生代的GC頻率和時間增加,

配置引數:

 //新生代可容納的最大物件,大于則直接會分配到老年代,0代表沒有限制,
  -XX:PretenureSizeThreshold=1000000 

7.7、調整GC的觸發時機

現象:CMS,G1 經常 Full GC,程式卡頓嚴重,

原因:G1和CMS 部分GC階段是并發進行的,業務執行緒和垃圾收集執行緒一起作業,也就說明垃圾收集的程序中業務執行緒會生成新的物件,所以在GC的時候需要預留一部分記憶體空間來容納新產生的物件,如果這個時候記憶體空間不足以容納新產生的物件,那么JVM就會停止并發收集暫停所有業務執行緒(STW)來保證垃圾收集的正常運行,這個時候可以調整GC觸發的時機(比如在老年代占用60%就觸發GC),這樣就可以預留足夠的空間來讓業務執行緒創建的物件有足夠的空間分配,

注意:提早觸發GC會增加老年代GC的頻率,

配置引數:

 //使用多少比例的老年代后開始CMS收集,默認是68%,如果頻繁發生SerialOld卡頓,應該調小
 -XX:CMSInitiatingOccupancyFraction
 ?
 //G1混合垃圾回收周期中要包括的舊區域設定占用率閾值,默認占用率為 65%
 -XX:G1MixedGCLiveThresholdPercent=65 

7.8、調整 JVM本地記憶體大小

現象:GC的次數、時間和回收的物件都正常,堆記憶體空間充足,但是報OOM

原因: JVM除了堆記憶體之外還有一塊堆外記憶體,這片記憶體也叫本地記憶體,可是這塊記憶體區域不足了并不會主動觸發GC,只有在堆記憶體區域觸發的時候順帶會把本地記憶體回收了,而一旦本地記憶體分配不足就會直接報OOM例外,

注意: 本地記憶體例外的時候除了上面的現象之外,例外資訊可能是OutOfMemoryError:Direct buffer memory, 解決方式除了調整本地記憶體大小之外,也可以在出現此例外時進行捕獲,手動觸發GC(System.gc()),

配置引數:

 XX:MaxDirectMemorySize

8、JVM調優實體

以下是整理自網路的一些JVM調優實體:

8.1、網站流量瀏覽量暴增后,網站反應頁面響很慢

1、問題推測:在測驗環境測速度比較快,但是一到生產就變慢,所以推測可能是因為垃圾收集導致的業務執行緒停頓,

2、定位:為了確認推測的正確性,在線上通過jstat -gc 指令 看到JVM進行GC 次數頻率非常高,GC所占用的時間非常長,所以基本推斷就是因為GC頻率非常高,所以導致業務執行緒經常停頓,從而造成網頁反應很慢,

3、解決方案:因為網頁訪問量很高,所以物件創建速度非常快,導致堆記憶體容易填滿從而頻繁GC,所以這里問題在于新生代記憶體太小,所以這里可以增加JVM記憶體就行了,所以初步從原來的2G記憶體增加到16G記憶體,

4、第二個問題:增加記憶體后的確平常的請求比較快了,但是又出現了另外一個問題,就是不定期的會間斷性的卡頓,而且單次卡頓的時間要比之前要長很多,

5、問題推測:練習到是之前的優化加大了記憶體,所以推測可能是因為記憶體加大了,從而導致單次GC的時間變長從而導致間接性的卡頓,

6、定位:還是通過jstat -gc 指令 查看到 的確FGC次數并不是很高,但是花費在FGC上的時間是非常高的,根據GC日志 查看到單次FGC的時間有達到幾十秒的,

7、解決方案: 因為JVM默認使用的是PS+PO的組合,PS+PO垃圾標記和收集階段都是STW,所以記憶體加大了之后,需要進行垃圾回收的時間就變長了,所以這里要想避免單次GC時間過長,所以需要更換并發類的收集器,因為當前的JDK版本為1.7,所以最后選擇CMS垃圾收集器,根據之前垃圾收集情況設定了一個預期的停頓的時間,上線后網站再也沒有了卡頓問題,

8.2、后臺匯出資料引發的OOM

**問題描述:**公司的后臺系統,偶發性的引發OOM例外,堆記憶體溢位,

1、因為是偶發性的,所以第一次簡單的認為就是堆記憶體不足導致,所以單方面的加大了堆記憶體從4G調整到8G,

2、但是問題依然沒有解決,只能從堆記憶體資訊下手,通過開啟了-XX:+HeapDumpOnOutOfMemoryError引數 獲得堆記憶體的dump檔案,

3、VisualVM 對 堆dump檔案進行分析,通過VisualVM查看到占用記憶體最大的物件是String物件,本來想跟蹤著String物件找到其參考的地方,但dump檔案太大,跟蹤進去的時候總是卡死,而String物件占用比較多也比較正常,最開始也沒有認定就是這里的問題,于是就從執行緒資訊里面找突破點,

4、通過執行緒進行分析,先找到了幾個正在運行的業務執行緒,然后逐一跟進業務執行緒看了下代碼,發現有個引起我注意的方法,匯出訂單資訊,

5、因為訂單資訊匯出這個方法可能會有幾萬的資料量,首先要從資料庫里面查詢出來訂單資訊,然后把訂單資訊生成excel,這個程序會產生大量的String物件,

6、為了驗證自己的猜想,于是準備登錄后臺去測驗下,結果在測驗的程序中發現到處訂單的按鈕前端居然沒有做點擊后按鈕置灰互動事件,結果按鈕可以一直點,因為匯出訂單資料本來就非常慢,使用的人員可能發現點擊后很久后頁面都沒反應,結果就一直點,結果就大量的請求進入到后臺,堆記憶體產生了大量的訂單物件和EXCEL物件,而且方法執行非常慢,導致這一段時間內這些物件都無法被回收,所以最終導致記憶體溢位,

7、知道了問題就容易解決了,最終沒有調整任何JVM引數,只是在前端的匯出訂單按鈕上加上了置灰狀態,等后端回應之后按鈕才可以進行點擊,然后減少了查詢訂單資訊的非必要欄位來減少生成物件的體積,然后問題就解決了,

8.3、單個快取資料過大導致的系統CPU飚高

1、系統發布后發現CPU一直飚高到600%,發現這個問題后首先要做的是定位到是哪個應用占用CPU高,通過top 找到了對應的一個java應用占用CPU資源600%,

2、如果是應用的CPU飚高,那么基本上可以定位可能是鎖資源競爭,或者是頻繁GC造成的,

3、所以準備首先從GC的情況排查,如果GC正常的話再從執行緒的角度排查,首先使用jstat -gc PID 指令列印出GC的資訊,結果得到得到的GC 統計資訊有明顯的例外,應用在運行了才幾分鐘的情況下GC的時間就占用了482秒,那么問這很明顯就是頻繁GC導致的CPU飚高,

4、定位到了是GC的問題,那么下一步就是找到頻繁GC的原因了,所以可以從兩方面定位了,可能是哪個地方頻繁創建物件,或者就是有記憶體泄露導致記憶體回收不掉,

5、根據這個思路決定把堆記憶體資訊dump下來看一下,使用jmap -dump 指令把堆記憶體資訊dump下來(堆記憶體空間大的慎用這個指令否則容易導致會影回應用,因為我們的堆記憶體空間才2G所以也就沒考慮這個問題了),

6、把堆記憶體資訊dump下來后,就使用visualVM進行離線分析了,首先從占用記憶體最多的物件中查找,結果排名第三看到一個業務VO占用堆記憶體約10%的空間,很明顯這個物件是有問題的,

7、通過業務物件找到了對應的業務代碼,通過代碼的分析找到了一個可疑之處,這個業務物件是查看新聞資訊資訊生成的物件,由于想提升查詢的效率,所以把新聞資訊保存到了redis快取里面,每次呼叫資訊介面都是從快取里面獲取,

8、把新聞保存到redis快取里面這個方式是沒有問題的,有問題的是新聞的50000多條資料都是保存在一個key里面,這樣就導致每次呼叫查詢新聞介面都會從redis里面把50000多條資料都拿出來,再做篩選分頁拿出10潭訓傳給前端,50000多條資料也就意味著會產生50000多個物件,每個物件280個位元組左右,50000個物件就有13.3M,這就意味著只要查看一次新聞資訊就會產生至少13.3M的物件,那么并發請求量只要到10,那么每秒鐘都會產生133M的物件,而這種大物件會被直接分配到老年代,這樣的話一個2G大小的老年代記憶體,只需要幾秒就會塞滿,從而觸發GC,

9、知道了問題所在后那么就容易解決了,問題是因為單個快取過大造成的,那么只需要把快取減小就行了,這里只需要把快取以頁的粒度進行快取就行了,每個key快取10條作為回傳給前端1頁的資料,這樣的話每次查詢新聞資訊只會從快取拿出10條資料,就避免了此問題的 產生,

8.4、CPU經常100% 問題定位

問題分析:CPU高一定是某個程式長期占用了CPU資源,

1、所以先需要找出那個進行占用CPU高,

 top  列出系統各個行程的資源占用情況,

2、然后根據找到對應進行里哪個執行緒占用CPU高,

 top -Hp 行程ID   列出對應行程里面的執行緒占用資源情況

3、找到對應執行緒ID后,再列印出對應執行緒的堆疊資訊

printf "%x\n"  PID    把執行緒ID轉換為16進制,
 jstack PID 列印出行程的所有執行緒資訊,從列印出來的執行緒資訊中找到上一步轉換為16進制的執行緒ID對應的執行緒資訊,

4、最后根據執行緒的堆疊資訊定位到具體業務方法,從代碼邏輯中找到問題所在,

查看是否有執行緒長時間的watting 或blocked
 如果執行緒長期處于watting狀態下, 關注watting on xxxxxx,說明執行緒在等待這把鎖,然后根據鎖的地址找到持有鎖的執行緒,

8.5、記憶體飚高問題定位

分析: 記憶體飚高如果是發生在java行程上,一般是因為創建了大量物件所導致,持續飚高說明垃圾回收跟不上物件創建的速度,或者記憶體泄露導致物件無法回收,

1、先觀察垃圾回收的情況

jstat -gc PID 1000 查看GC次數,時間等資訊,每隔一秒列印一次,
  
 jmap -histo PID | head -20   查看堆記憶體占用空間最大的前20個物件型別,可初步查看是哪個物件占用了記憶體,

如果每次GC次數頻繁,而且每次回收的記憶體空間也正常,那說明是因為物件創建速度快導致記憶體一直占用很高;如果每次回收的記憶體非常少,那么很可能是因為記憶體泄露導致記憶體一直無法被回收,

2、匯出堆記憶體檔案快照

jmap -dump:live,format=b,file=/home/myheapdump.hprof PID  dump堆記憶體資訊到檔案,

3、使用visualVM對dump檔案進行離線分析,找到占用記憶體高的物件,再找到創建該物件的業務代碼位置,從代碼和業務場景中定位具體問題,

8.6、資料分析平臺系統頻繁 Full GC

平臺主要對用戶在 App 中行為進行定時分析統計,并支持報表匯出,使用 CMS GC 演算法,

資料分析師在使用中發現系統頁面打開經常卡頓,通過 jstat 命令發現系統每次 Young GC 后大約有 10% 的存活物件進入老年代,

原來是因為 Survivor 區空間設定過小,每次 Young GC 后存活物件在 Survivor 區域放不下,提前進入老年代,

通過調大 Survivor 區,使得 Survivor 區可以容納 Young GC 后存活物件,物件在 Survivor 區經歷多次 Young GC 達到年齡閾值才進入老年代,

調整之后每次 Young GC 后進入老年代的存活物件穩定運行時僅幾百 Kb,Full GC 頻率大大降低,

8.7、業務對接網關 OOM

網關主要消費 Kafka 資料,進行資料處理計算然后轉發到另外的 Kafka 佇列,系統運行幾個小時候出現 OOM,重啟系統幾個小時之后又 OOM,

通過 jmap 匯出堆記憶體,在 eclipse MAT 工具分析才找出原因:代碼中將某個業務 Kafka 的 topic 資料進行日志異步列印,該業務資料量較大,大量物件堆積在記憶體中等待被列印,導致 OOM,

8.8、鑒權系統頻繁長時間 Full GC

系統對外提供各種賬號鑒權服務,使用時發現系統經常服務不可用,通過 Zabbix 的監控平臺監控發現系統頻繁發生長時間 Full GC,且觸發時老年代的堆記憶體通常并沒有占滿,發現原來是業務代碼中呼叫了 System.gc(),




參考:

【1】:周志明編著《深入理解Java虛擬機:JVM高級特性與最佳實踐》

【2】:《實戰JAVA虛擬機 JVM故障診斷與性能優化》

【3】:JVM性能調優詳解

【4】:如何合理的規劃一次jvm性能調優

【5】:關于GC原理和性能調優實踐,看這一篇就夠了

【6】:Java 應用性能調優實踐

【7】:JVM實戰:JVM調優策略

【8】:一般的Java專案需要JVM調優嗎?

【9】:Java8 JVM引數解讀

【10】:JVM引數設定-jdk8引數設定

【11】:JVM面試問題系列:JVM 配置常用引數和常用 GC 調優策略

【12】:Java1.8的jvm引數官方網站地址

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/275042.html

標籤:其他

上一篇:初識C語言之演算法設計篇——帶你走進編程世界的小院!

下一篇:Fdog系列(二):html寫完注冊頁面之后怎么辦,用java寫后臺回應呀。

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more