JVM調(diào)優(yōu)

一、調(diào)優(yōu)目的

  1. 避免OOM
  2. 減少Full GC

二、判斷標(biāo)準(zhǔn)

  • 每次YGC耗時(shí)在100ms以內(nèi)
  • 每次FGC耗時(shí)在1s以內(nèi)
  • FGC頻率最多幾小時(shí)一次

三、調(diào)優(yōu)依據(jù)

3.1 運(yùn)行日志

3.2 GC情況(jstat)

  • jps -l 查看運(yùn)行的java程序進(jìn)程
  • jstat(JVM statistics Monitor)命令查看各種狀態(tài),包含GC狀態(tài)

jstat -gc 25772 2000 20:代表每隔2000ms輸出一次進(jìn)程25772的內(nèi)存情況,總共輸出20次

C:\Users\Administrator>jstat -gc 25772 2000 20
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
55808.0 9216.0  0.0   8896.0 1260544.0 146741.4  344064.0   266516.3  134912.0 127714.6 15872.0 14629.5     25    0.570   4      0.393    0.964

S0C:年輕代中第一個(gè)survivor(幸存區(qū))的容量 (KB)
S1C:年輕代中第二個(gè)survivor(幸存區(qū))的容量 (KB)
S0U:年輕代中第一個(gè)survivor(幸存區(qū))目前已使用空間 (KB)
S1U:年輕代中第二個(gè)survivor(幸存區(qū))目前已使用空間 (KB)
EC:年輕代中Eden(伊甸園)的容量 (KB)
EU:年輕代中Eden(伊甸園)目前已使用空間 (KB)
OC:Old代的容量 (KB)
OU:Old代目前已使用空間 (KB)
MC:元空間的容量 (KB)
MU:元空間目前已使用空間 (KB)
CCSC:壓縮類空間的容量 (KB)
CCSU:壓縮類空間已使用空間 (KB)
YGC:從應(yīng)用程序啟動到采樣時(shí)年輕代中g(shù)c次數(shù)
YGCT:從應(yīng)用程序啟動到采樣時(shí)年輕代中g(shù)c所用時(shí)間(秒)
FGC:從應(yīng)用程序啟動到采樣時(shí)old代(全gc)gc次數(shù)
FGCT:從應(yīng)用程序啟動到采樣時(shí)old代(全gc)gc所用時(shí)間(秒)
GCT:從應(yīng)用程序啟動到采樣時(shí)gc用的總時(shí)間(秒)

  • java程序啟動時(shí)可以配置gc日志的相關(guān)參數(shù),將其打印出來,或保存到本地

3.3 線程快照(jstack可以用來排查CPU飆升、死鎖、死循環(huán)等)

jstack命令(可使用該命令導(dǎo)出到本地文件)

C:\Users\Administrator>jstack 25772
2024-03-07 11:38:09
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.341-b10 mixed mode):
...
Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x0000017bfeca3e80 (object 0x000000008a0e36e8, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x0000017bfeca5e80 (object 0x000000008a0e36f8, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at TestDeadLock$Thread2.run(TestDeadLock.java:40)
        - waiting to lock <0x000000008a0e36e8> (a java.lang.Object)
        - locked <0x000000008a0e36f8> (a java.lang.Object)
        at java.lang.Thread.run(java.base@9.0.4/Thread.java:844)
"Thread-0":
        at TestDeadLock$Thread1.run(TestDeadLock.java:22)
        - waiting to lock <0x000000008a0e36f8> (a java.lang.Object)
        - locked <0x000000008a0e36e8> (a java.lang.Object)
        at java.lang.Thread.run(java.base@9.0.4/Thread.java:844)

Found 1 deadlock.
...
...

3.4 堆快照(jmap)

jmap命令查看當(dāng)前堆情況,或者保存到本地文件

jmap -histo pid 輸出堆的直方圖,包含類名、對象數(shù)量、占用大小
jmap -histo:live pid 同上,只輸出存活對象
jmap -dump:format=b,file=xxx.hprof pid 導(dǎo)出dump文件
jmap -heap pid查看堆的使用占比情況

C:\Users\Administrator>jmap -heap 25772
Attaching to process ID 25772, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.341-b10

using thread-local object allocation.
Parallel GC with 10 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4227858432 (4032.0MB)
   NewSize                  = 88080384 (84.0MB)
   MaxNewSize               = 1409286144 (1344.0MB)
   OldSize                  = 176160768 (168.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 1290797056 (1231.0MB)
   used     = 218955584 (208.81231689453125MB)
   free     = 1071841472 (1022.1876831054688MB)
   16.96282021888962% used
From Space:
   capacity = 9437184 (9.0MB)
   used     = 9109504 (8.6875MB)
   free     = 327680 (0.3125MB)
   96.52777777777777% used
To Space:
   capacity = 57147392 (54.5MB)
   used     = 0 (0.0MB)
   free     = 57147392 (54.5MB)
   0.0% used
PS Old Generation
   capacity = 352321536 (336.0MB)
   used     = 272912736 (260.2698669433594MB)
   free     = 79408800 (75.73013305664062MB)
   77.46126992361886% used

56894 interned Strings occupying 5551600 bytes.

3.5 查看和調(diào)整虛擬機(jī)運(yùn)行參數(shù)(jinfo)

jinfo pid 輸出全部的參數(shù)和系統(tǒng)屬性
jinfo -flags pid 輸出全部的參數(shù)
ps -aux 參看進(jìn)程的啟動命令

C:\Users\Administrator>jinfo 21252
Attaching to process ID 21252, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.341-b10
Java System Properties:

spring.output.ansi.enabled = always
java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.341-b10
...

四、對癥下藥

  1. 如果發(fā)現(xiàn)新生代GC頻繁,可以調(diào)大新生代內(nèi)存空間。-Xms1024m -Xmx1024m 可以直接調(diào)整heap大小,默認(rèn)新生代占1/3
  2. 如果FGC比較頻繁,且回收后老年代占用較小,原因可能是Survivor From區(qū)太小,很多臨時(shí)變量被直接放入了老年代??梢酝ㄟ^-XX:SurvivorRatio=4 -XX:-UseAdaptiveSizePolicy將默認(rèn)Eden:Survivor=8:1改為4:1
  3. 調(diào)大后,GC次數(shù)少了,但每次GC時(shí)間變長的話,可以考慮換一個(gè)垃圾收集器,比如把并行收集器改為并發(fā)收集器,兩者雖都是多線程GC,但后者可以在GC某些階段不暫停應(yīng)用,一邊GC一邊運(yùn)行
  4. 如果內(nèi)存空間大小合理,但GC依然頻繁,那可能是堆里的對象有問題,可以通過jmap查看堆里的對象,找到占用較多的對象,然后找到相關(guān)業(yè)務(wù)代碼進(jìn)行分析

五、內(nèi)存泄露的排查思路

  1. jstat查看堆和gc是否有異常情況
  2. 使用MAT工具分析
  3. MAT工具會給出一個(gè)分析結(jié)果,并定位到有問題的類
  4. 常見原因有:循環(huán)引用、內(nèi)存對象泄露沒有被銷毀、動態(tài)分配內(nèi)存以后未釋放、長期持有對象引用、資源未關(guān)閉等

六、CPU飆升的排查實(shí)踐

  1. top -Hp pid:查找cpu占用最高的線程號nid
  2. printf '%x\n' 線程id:轉(zhuǎn)為16進(jìn)制的線程號nid
  3. jstack pid | grep -A 200 0x[nid],例如:jstack 41 | grep -A 200 0x61

七、OOM排查實(shí)踐

  1. 最開始發(fā)現(xiàn)服務(wù)不可用,日志顯示OOM
  2. jstat -gc pid查看FGC次數(shù)特別多,jstack pid未發(fā)現(xiàn)明顯異常,jmap -heap pid發(fā)現(xiàn)堆大小為512M(容器總內(nèi)存為2G,默認(rèn)占1/4
  3. 同時(shí)把heap dump下來(保存現(xiàn)場),并用MAT工具分析內(nèi)存泄漏和查看占用多的對象的GC Root,未發(fā)現(xiàn)明顯異常
  4. 嘗試把堆內(nèi)存調(diào)大到1G:-Xms1024m -Xmx1024m(默認(rèn)新生代:老年代=1:2
  5. 重啟服務(wù)后,雖然沒有OOM了,但FGC依然頻繁,且回收后老年代占用較小
  6. 懷疑是Survivor區(qū)太小,導(dǎo)致每次YGC時(shí),有很多對象直接放入了老年代
  7. 嘗試調(diào)大Survivor區(qū):-XX:SurvivorRatio=4 -XX:-UseAdaptiveSizePolicy將默認(rèn)Eden:Survivor=8:1改為4:1
  8. 運(yùn)行了17個(gè)小時(shí)后觀察:FGC次數(shù)6次,總時(shí)間為1.218秒,YGC次數(shù)1010次,總時(shí)間為29.822秒。問題解決

八、其它

JDK1.8對應(yīng)的MAT工具版本:https://eclipse.dev/mat/previousReleases.php

image.png

;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JVM 調(diào)優(yōu)概述 性能定義 吞吐量 - 指不考慮 GC 引起的停頓時(shí)間或內(nèi)存消耗,垃圾收集器能支撐應(yīng)用達(dá)到的最高性...
    裘馬輕狂大帥閱讀 312評論 0 1
  • JVM 調(diào)優(yōu)概述 性能定義 吞吐量 - 指不考慮 GC 引起的停頓時(shí)間或內(nèi)存消耗,垃圾收集器能支撐應(yīng)用達(dá)到的最高性...
    裘馬輕狂大帥閱讀 293評論 0 1
  • GC和GC Tuning 原作者:馬士兵老師http://mashibing.com GC的基礎(chǔ)知識 1.什么是垃...
    fat32jin閱讀 1,014評論 0 0
  • 運(yùn)用jvm自帶的命令可以方便的在生產(chǎn)監(jiān)控和打印堆棧的日志信息幫忙我們來定位問題!雖然jvm調(diào)優(yōu)成熟的工具已經(jīng)有很多...
    王知無閱讀 822評論 1 1
  • 目錄 前置啟動程序 事先啟動一個(gè)web應(yīng)用程序,用jps查看其進(jìn)程id,接著用各種jdk自帶命令優(yōu)化應(yīng)用 jvm內(nèi)...
    米不開朗基羅閱讀 880評論 0 0

友情鏈接更多精彩內(nèi)容