jvm 命令使用調優 通過jstat、jmap對java程序進行性能調優


轉載: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><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>

 

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><3>

<4><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><5>

一眼看去,String對象穩居第一,好吧,項目中對String的處理確實有不少問題,但是,排名第三的這是什么東西?!

在代碼中一查,這個對象只在一個上傳文件中的Servlet中被用到了一次,很明顯的內存泄漏!

 

3.問題解決

很明顯,jspsmart這個插件正是內存泄漏的元凶,網上查找資料之后確定,jspsmart由於自身bug,在長時間運行后會產生兩個嚴重問題:
a. 客戶端在未完成上傳時便主動終止了上傳操作,此時客戶端不再上傳文件,但是服務端仍然會占用InputStream,並且不會主動釋放,造成cpu占用率100%,猜測a證實
b. 客戶上傳的文件損壞,在創建File對象時無法找到文件終止標識,因此一直在創建新的File對象,造成內存泄漏。
 
由此,服務器內存溢出和cpu占用率100%兩個問題都找到了關鍵所在,接下來的工作就像對簡單了,使用apache的開源項目commons-upload替換原來的jspsmart來完成文件上傳的工作,替換過后,系統資源占用正常,附上bug修復后的jmap截圖
<6> <6>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM