轉載:http://blog.csdn.net/jerry024/article/details/8507589
轉載: https://blog.csdn.net/zhaozheng7758/article/details/8623530
通過jstat、jmap對java程序進行性能調優
第一次寫技術博客,本文記錄了我對服務器cpu占用率100%問題的排查過程和最終結果,算是一篇學習日志。
本人屬於學習中的菜鳥碼農,如有什么理解上的偏差的,還請各路大神指正。
關鍵詞:
jstat , jmap, heap, GC, smartupload, 內存泄漏
1. 背景
硬件供應商多次反映,在tomcat啟動一段時間后,經常出現cpu占用率100%,且重啟前一直保持在100%的情況。在重啟后cpu占用率回落,但是一段時間后再次出現問題。
下圖為cpu占用率100%時使用top命令的截圖,可以看到java進程的cpu占用率幾經幾乎達到了400%(服務器為4核cpu)
<1>
2.問題定位
2.1問題猜測
對於cpu占用率100%的情況,產生以下兩種猜測:
a. 程序長時間占用系統IO,導致CPU占用率100%
b.程序存在嚴重內存泄露,導致jvm頻繁執行full GC,從而使cpu占用率提高,造成服務器假死
2.2jvm的內存管理和垃圾回收機制
java對內存的管理主要分為兩種:棧(stack)和堆(heap) 方法區,程序計數器等不做討論
stack: 在每個線程啟動時由jvm自動分配固定大小的地址,stack內主要保存操作符,值對象(int,float等基礎數據類型),和引用對象的指針由於stack固定大小,且主要操作為push和pop,並不涉及到垃圾回收等問題,因此不做展開。
heap:堆在線程執行過程中自動分配大小,大小可以隨時改變,主要用於保存對象(Object),對象在結束生命周期結束之后便會由JVM的垃圾回收機制自動進行回收堆可分為3大部分年輕代(Young Generation)、年老代(Old Generation)和持久代(Permanent Generation)。
年輕代:年輕代又可分為3個小區:Eden,兩個等大的Survior區(from 和 to)。其中Eden主要存放新建的對象,當Eden區無法再存放更多的對象時,jvm會發起年輕代GC(Minor GC),釋放Eden中的對象,Minor GC的特點是發生頻率高,執行速度也極快,對系統效率的影響並不是很大。當Eden區中的對象經過一次Minor GC仍然沒有被釋放時,這部分對象將被移入Survior區(對象可能進入from區,也可能進入to區,有具體算法,此處不做展開)。當Survior區中的對象經歷過數次的Minor GC之后仍然存活,將被移入年老代。
年老代:年老代中用來存放從年輕代過來的長時間使用的對象,大部分的JVM內存溢出錯誤均發生在這個區域。當年老代被過度占用,無法存放下更多的數據時,jvm會發起一次年老代GC(Major GC\Full GC),該類型GC會釋放年老代中的資源,雖然該GC觸發頻率很低,但是對硬件資源的消耗較高,且Full GC過程中會暫停該線程的執行。如果系統中存在內存泄露,頻繁的觸發Full GC,將會嚴重的占用服務器資源,造成應用的假死,這也是我之前猜測b的依據。
持久代:持久代中用於存放jvm的反射類等,如class等,此區域對GC的影響不大,也不大會發生內存溢出的情況。
下圖是引用網上的一張圖片,更形象的描述了heap區的構成
<2>
2.3jstat、jmap實戰
ok,在弄清楚jvm的GC機制之后,就有了努力的方向了,為了弄清楚GC具體的工作情況,就要使用到jstat命令了。
jstat(Java Virtual Machine Statistics Monitoring Tool)是jdk自帶的監控工具,位置在%JAVA_HOME%/bin 下,命令使用方法為
jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]
下圖是我對系統中tomcat進程的監控情況
<3>
<4>
命令中 -gcutil表示統計GC情況,5125為tomcat的pid,10000表示沒10s統計一次,5表示一共統計5次
結果中s0為from區,s1為to區,E為Eden區,O為Old區,P為Permanent區,YGC為yong GC次數,YGCT為yong GC執行的總時間,FGC為Full GC次數,FGCT為Full GC總時間,GCT為GC總時間。
上面圖<3>為cpu占用率高時的截圖,圖<4>為正常情況下截圖。
很明顯的看到GC次數相差不大的情況下,GC耗時存在很大的差距,推測此時系統中可能存在內存泄漏的情況。
為了確定jvm中具體有哪些生存的對象,就需要用到jdk自帶的另一個監控工具,jmap了
jmap(Memory Map)用於監控系統內存中存活的對象。
使用命令: jmap -histo:live 5125>> /opt/jmap.txt
其中5125為pid ,由於數據較多,將數據保存在txt文件中進行分析,下圖列出了對內存占用排名前幾的對象
<5>
一眼看去,String對象穩居第一,好吧,項目中對String的處理確實有不少問題,但是,排名第三的這是什么東西?!
在代碼中一查,這個對象只在一個上傳文件中的Servlet中被用到了一次,很明顯的內存泄漏!
3.問題解決
