JVM故障分析及效能優化系列之三:jstat命令的使用及VM Thread分析

語言: CN / TW / HK

前面提到了一個 使用jstack的shell指令碼 ,通過命令可以很快地定位到指定執行緒對應的堆疊資訊。

使用jstat命令

當伺服器CPU100%的時候,通過定位佔用資源最大的執行緒定位到 VM Thread:

<code>"VM Thread" prio=10 tid=0x00007fbea80d3800 nid=0x5e9 runnable
</code><button>複製</button>

這個時候需要使用 jstat -gc <pid> <period> <times> 命令檢視gc的資訊,顯示結果如下:

<code>S0C    S1C    S0U    S1U      EC       EU      OC         OU       PC        PU       YGC     YGCT    FGC   FGCT       GCT
64.0   64.0   0.0    0.0   332992.0   0.0    666304.0   73192.5   83968.0   83967.9   6893   17.576  6882   2705.923  2723.499
</code><button>複製</button>

結果中每個專案的含義可以參考官方對 jstat 的文件,簡單翻譯如下:

  • S0C: Young Generation第一個survivor space的記憶體大小 (kB).
  • S1C: Young Generation第二個survivor space的記憶體大小 (kB).
  • S0U: Young Generation第一個Survivor space當前已使用的記憶體大小 (kB).
  • S1U: Young Generation第二個Survivor space當前已經使用的記憶體大小 (kB).
  • EC: Young Generation中eden space的記憶體大小 (kB).
  • EU: Young Generation中Eden space當前已使用的記憶體大小 (kB).
  • OC: Old Generation的記憶體大小 (kB).
  • OU: Old Generation當前已使用的記憶體大小 (kB).
  • PC: Permanent Generation的記憶體大小 (kB)
  • PU: Permanent Generation當前已使用的記憶體大小 (kB).
  • YGC: 從啟動到取樣時Young Generation GC的次數
  • YGCT: 從啟動到取樣時Young Generation GC所用的時間 (s).
  • FGC: 從啟動到取樣時Old Generation GC的次數.
  • FGCT: 從啟動到取樣時Old Generation GC所用的時間 (s).
  • GCT: 從啟動到取樣時GC所用的總時間 (s).

JDK8的結果稍微有所不同,結果含義可以參考: http://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html。

JVM記憶體模型

上面中的Young Generation、Permanent Generation和Old Generation等概念有一些混亂,這裡簡要的進行說明。簡單來說,JVM記憶體由堆(Heap)和非堆(Non-heap)記憶體組成,前者共執行在JVM之上的程式使用,後者供JVM自己使用。

堆記憶體的組成如下:

非堆記憶體由 Permanent Generation 和 Code Cache 兩部分組成:

  • Permanent Generation(持久代): 儲存虛擬機器自己的靜態(refective)資料,主要存放載入的Class類級別靜態物件如class本身,method,field等等。permanent generation空間不足會引發full GC;

  • Code Cache: 用於編譯和儲存原生代碼(native code)的記憶體,JVM內部處理或優化。

JVM記憶體引數設定

堆記憶體設定

  • 堆記憶體(總的)由 -Xms-Xmx 分別設定最小和最大堆記憶體

  • New Generation 由 -Xmn 設定, -XX:SurvivorRatio=m 設定 Eden和 兩個Survivor區的大小比值; -XX:NewRatio=n 設定 New Generation 和 Old Generation 的大小比值。

  • 每個執行緒的堆疊大小由 ·-Xss· 設定,JDK5.0以後每個執行緒堆疊大小為1M,以前每個執行緒堆疊大小為256K。在相同實體記憶體下,減小這個值能生成更多的執行緒。但是作業系統對一個程序內的執行緒數還是有限制的,不能無限生成,經驗值在3000~5000左右。

非堆記憶體設定

非堆記憶體由 -XX:PermSize=n-XX:MaxPermSize=n 分別設定最小和最大非堆記憶體大小

日誌分析

介紹完上面的概念之後,我們再來看最上面的日誌資訊,有兩個地方有問題:

一是FGC(完全GC)的數量太大了,正常來說FGC應該佔整個GC(YGC+FGC)的1%到5%才正常,上面日誌上完全GC的次數太多了;二是日誌中PU的值太大了,基本上已經達到設定的PC了,因此需要增大MaxPermSize的值。

不過這只是權宜之計,出現這麼大的非堆記憶體,肯定什麼地方出現了問題,還需要進一步找到佔用記憶體的原因,這也是後面文章所要說的。

原文連結: https://www.javatang.com/archives/2017/10/20/12131956.html