原文地址:http://www.javatang.com
使用jstat命令
當服務器CPU100%的時候,通過定位占用資源最大的線程定位到 VM Thread:
"VM Thread" prio=10 tid=0x00007fbea80d3800 nid=0x5e9 runnable
這個時候需要使用 jstat -gc <pid> <period> <times>
命令查看gc的信息,顯示結果如下:
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
結果中每個項目的含義可以參考官方對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的值。
不過這只是權宜之計,出現這么大的非堆內存,肯定什么地方出現了問題,還需要進一步找到占用內存的原因,這也是后面文章所要說的。