轉載:http://blog.csdn.net/jerry024/article/details/8507589html
轉載: https://blog.csdn.net/zhaozheng7758/article/details/8623530java
第一次寫技術博客,本文記錄了我對服務器cpu佔用率100%問題的排查過程和最終結果,算是一篇學習日誌。算法
本人屬於學習中的菜鳥碼農,若有什麼理解上的誤差的,還請各路大神指正。apache
jstat , jmap, heap, GC, smartupload, 內存泄漏tomcat
硬件供應商屢次反映,在tomcat啓動一段時間後,常常出現cpu佔用率100%,且重啓前一直保持在100%的狀況。在重啓後cpu佔用率回落,可是一段時間後再次出現問題。服務器
下圖爲cpu佔用率100%時使用top命令的截圖,能夠看到java進程的cpu佔用率幾經幾乎達到了400%(服務器爲4核cpu)jvm
<1>jsp
對於cpu佔用率100%的狀況,產生如下兩種猜想:工具
a. 程序長時間佔用系統IO,致使CPU佔用率100%性能
b.程序存在嚴重內存泄露,致使jvm頻繁執行full GC,從而使cpu佔用率提升,形成服務器假死
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>
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中被用到了一次,很明顯的內存泄漏!