java進程 cpu100%問題排查


排查java進程cpu100%的大致過程

之前遇到過
之前也遇到過cpu 100%的問題,原因是while循環,死循環了,一直占有cpu。

cpu為什么會100%

我們都知道cpu是時分(time division)的,操作系統里有很多線程,每個線程的運行時間由cpu決定,cpu會分給每個線程一個時間片,時間片是一個很短的時間長度,

如果在時間片內,線程一直占有,則是100%;

我們應該意識到,cpu運行速度很快(主頻非常高),除非密集型耗費cpu的運算,其它類型任務都會在小於時間片的時間內結束。

 

cpu 100%大致排查過程
排查java cpu100%的問題,大致步驟是固定的,

首先找到占用cpu的進程,

如果是java進程,

則繼續查看是哪個線程占用cpu,

然后根據線程id從線程棧中找到對應線程棧,

到這里,問題基本也就解決了。

故事背景

今天后台管理系統出現cpu 100%,這個問題間歇性出現,后台管理系統使用ssm(spring+springmvc+mybatis)+shiro實現,用戶量很小,所以可以排除高並發導致。接下來,我們按照前述排查步驟,進行排查。

找到cpu 100%的進程

登錄linux服務器找到占用cpu的進程,使用top

top

 

找出服務器的所有java進程,

ps -ef | grep java

 

或者使用

jps

 

經對比,占用cpu的進程是java進程,繼續挖,找出占用CPU的線程

top -H -p pid

 

在這里插入圖片描述
-H表示以線程的維度展示,默認以進程維度展示。

一共4個占用cpu的線程id 2944-2947,需要將線程id從十進制轉為十六進制,因為java線程棧文件中的線程id是十六進制。十進制 轉十六進制的命令是

echo "obase=16;number" | bc

obase(output base)是輸出的進制,number是輸入值,默認十進制,bc(An arbitrary precision calculator language)是任意進制轉換語言。
執行

echo "obase=16;2944" | bc

 

結果是

B80

 

導出棧

將java進程的線程棧導出,

jstack pid > pid.tdump

 

pid.tdump文件后綴名隨意,通常以tdump結尾。


在pid.tdump中找到nid=0XB80的線程,這4個線程都是gc線程。一般的cpu 100%問題到這就結束了,但是這次不一樣,因為這4個線程是gc 線程。

gc線程忙碌表示內存不夠用了,要進行內存回收,第一反應是java內存回收不了,導致一直gc。

 

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fa49c01f000 nid=0xb80 runnable 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fa49c020800 nid=0xb81 runnable 

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007fa49c022800 nid=0xb82 runnable 

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007fa49c024000 nid=0xb83 runnable 

導出堆

首先看一下堆的使用情況

jstat -gcutil pid

查看Java內存占用情況,發現老年代和伊甸區,使用率都90%多,但是存活區分配的很小大約500KB,並且基本沒有使用;

伊甸園區本來應該復制到存活區,但存活區過小,所以直接復制到老年代,但老年代已經沒有空間了,

所以,伊甸區越積累越多。那么到底是什么占着老年代不釋放呢?

沒辦法,導出java 堆來看看吧。如下將堆內存導出,只導出live的對象:

jmap -dump:live,format=b,file=pid.hprof pid

同樣的 文件后綴名可以是任意的,因為它也是二進制的,不過通常以hprof結尾。

jvisualvm分析快照

使用JAVA_HOME/bin/jvisualvm.exe,載入快照(文件----->載入—>文件類型(堆))
在這里插入圖片描述

 


按照大小排序,找出占用內存最大的類別,居然是字節數組,右擊在實例圖中顯示,

在這里插入圖片描述

 

我們發現字節數組的值都很規律,前半部分的字節數組基本都相同,我就想能不能把字節數組轉為字符串,這樣就能 知道字節數組是什么內容了,恰好左下角有個將字節數組另存為二進制文件的選項。


在這里插入圖片描述
將二進制文件轉為字符串,發現這些字節數組是 當前用戶的信息 ,如下

        File file = new File("C:\\Users\\DELL\\Desktop\\heap.bin");
        final FileInputStream fileInputStream = new FileInputStream(file);
        byte[] buffer = new byte[300];
        int len = -1;
        while((len=fileInputStream.read(buffer))!=-1){
            System.out.println(new String(buffer,0,len , "utf8"));
        }

結合項目分析

當前用戶信息放在session中,而session又放在ehcache和redis,check shiro的sessionDAO發現,

session銷毀時,只將redis的session刪除,而未將ehcache中的也刪除

另外查看ehcache中關於session cache配置,內存中元素個數是0,也就是不限制,這個很危險,並且配置的也不正確。

為了重現這個問題,重啟了tomcat,然后jmeter壓測登錄,發現老年區和伊甸區又滿了,在等待了3個小時(最大存活時間)后,cpu仍然100%。

 

RedisSessionDAO .java

public class RedisSessionDAO extends EnterpriseCacheSessionDAO {
	@Override
    public void doDelete(Session session) {
        //將redis中的session清除
    }
    }

 


ehcache.xml,shiro session ehcache配置可以參考shiro session設置了過期時間不起作用、無效;

<cache name="shiro-activeSessionCache" 
maxEntriesLocalHeap="0"
 eternal="false" 
 timeToIdleSeconds="10800"  //3小時
 timeToLiveSeconds="10800" //3小時
 overflowToDisk="false"/>

 


session過期時間為3小時,時間太長了

    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
    		<property name="globalSessionTimeout" value="10800000"></property>
   </bean>

改正

改正,session銷毀時,將ehcache中的session也銷毀
RedisSessionDAO .java

public class RedisSessionDAO extends EnterpriseCacheSessionDAO { @Override public void doDelete(Session session) { //將echache中的session也刪除 getActiveSessionsCache().remove(sessionId); //將redis中的session清除 } }

ehcache.xml

<cache name="shiro-activeSessionCache"
           maxEntriesLocalHeap="100"
           eternal="true"
           timeToIdleSeconds="0" timeToLiveSeconds="0"
           overflowToDisk="true"/>

 


去掉globalSessionTimeout,默認是30分鍾

    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
   </bean>

觀察

jmeter壓測一段時間后,老年代上升了,但過了1個小時(session調度器的默認執行間隔)后,old區,降下去了。到此為止,問題基本解決了。

 

 

必知必會java內存分析命令

上文提到的jstack、jmap、jstat、jvisualvm都位於 JAVA_HOME/bin 目錄下,這個目錄下有很多有用的工具。

疑問

雖然ehcache配置錯誤,但配了最大存活時間和最大空閑時間為3個小時,即使內存不限制數量,但到了3個小時ehcache應該會刪除才是,為什么沒刪除呢?

這個由於時間限制加上本身配置就是錯誤的應該配置為不限制,所以沒繼續追蹤下去。

 

上述原文:https://blog.csdn.net/wangjun5159/article/details/90414097

 

綜上:   “    由一個CUP占用率過高100% 的問題, 去理解Java垃圾回收機制 ”

              當java堆內存,得不到釋放時,越積越多,然后就會不斷觸發垃圾回收,

            (  操作系統里有很多線程,每個線程的運行時間由cpu決定,cpu會分給每個線程一個時間片,時間片是一個很短的時間長度, 如果在時間片內,線程一直占有,則是100%; )

 


免責聲明!

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



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