生產環境JAVA進程高CPU占用故障排查


問題描述:
生產環境下的某台tomcat7服務器,在剛發布時的時候一切都很正常,在運行一段時間后就出現CPU占用很高的問題,基本上是負載一天比一天高。

問題分析:
1,程序屬於CPU密集型,和開發溝通過,排除此類情況。
2,程序代碼有問題,出現死循環,可能性極大。

問題解決:
1,開發那邊無法排查代碼某個模塊有問題,從日志上也無法分析得出。
2,記得原來通過strace跟蹤的方法解決了一台PHP服務器CPU占用高的問題,但是通過這種方法無效,經過google搜索,發現可以通過下面的方法進行解決,那就嘗試下吧。

解決過程:
1,根據top命令,發現PID為2633的Java進程占用CPU高達300%,出現故障。

2,找到該進程后,如何定位具體線程或代碼呢,首先顯示線程列表,並按照CPU占用高的線程排序:
[root@localhost logs]#

1). ps -mp 2633 -o THREAD,tid,time | sort -rn

2).top H -p 2633
顯示結果如下:
USER     %CPU PRI SCNT WCHAN  USER SYSTEM   TID     TIME
root     10.5  19    - -         -      -  3626 00:12:48
root     10.1  19    - -         -      -  3593 00:12:16

找到了耗時最高的線程3626,占用CPU時間有12分鍾了!

3.將需要的線程ID轉換為16進制格式:
[root@localhost logs]# printf "%x\n" 3626
e18

4.最后打印線程的堆棧信息:
[root@localhost logs]#

1).jstack 2633 |grep e18 -A 30
2).kill -3 2633

5 查看這個線程所有系統調用 
strace -p 29679 


6.性能監控工具 Jprofiler 或者yourkit(推薦使用)


7.問題修改,找出有問題的代碼。
通過最近幾天的監控,CPU已經安靜下來了。

以我們最近出現的一個實際故障為例,介紹怎么定位和解決這類問題。

clip_image002

根據top命令,發現PID為28555的Java進程占用CPU高達200%,出現故障。

通過ps aux | grep PID命令,可以進一步確定是tomcat進程出現了問題。但是,怎么定位到具體線程或者代碼呢?

首先顯示線程列表:

ps -mp pid -o THREAD,tid,time

1

找到了耗時最高的線程28802,占用CPU時間快兩個小時了!

其次將需要的線程ID轉換為16進制格式:

printf "%x\n" tid

2

最后打印線程的堆棧信息:

jstack pid |grep tid -A 30

3

找到出現問題的代碼了!

現在來分析下具體的代碼:ShortSocketIO.readBytes(ShortSocketIO.java:106)

ShortSocketIO是應用封裝的一個用短連接Socket通信的工具類。readBytes函數的代碼如下:

public byte[] readBytes(int length) throws IOException {

    if ((this.socket == null) || (!this.socket.isConnected())) {

        throw new IOException("++++ attempting to read from closed socket");

    }

    byte[] result = null;

    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    if (this.recIndex >= length) {

           bos.write(this.recBuf, 0, length);

           byte[] newBuf = new byte[this.recBufSize];

           if (this.recIndex > length) {

               System.arraycopy(this.recBuf, length, newBuf, 0, this.recIndex - length);

           }

           this.recBuf = newBuf;

           this.recIndex -= length;

    } else {

           int totalread = length;

           if (this.recIndex > 0) {

                totalread -= this.recIndex;

                bos.write(this.recBuf, 0, this.recIndex);

                this.recBuf = new byte[this.recBufSize];

                this.recIndex = 0;

    }

    int readCount = 0;

    while (totalread > 0) {

         if ((readCount = this.in.read(this.recBuf)) > 0) {

                if (totalread > readCount) {

                      bos.write(this.recBuf, 0, readCount);

                      this.recBuf = new byte[this.recBufSize];

                      this.recIndex = 0;

               } else {

                     bos.write(this.recBuf, 0, totalread);

                     byte[] newBuf = new byte[this.recBufSize];

                     System.arraycopy(this.recBuf, totalread, newBuf, 0, readCount - totalread);

                     this.recBuf = newBuf;

                     this.recIndex = (readCount - totalread);

             }

             totalread -= readCount;

        }

   }

}

問題就出在標紅的代碼部分。如果this.in.read()返回的數據小於等於0時,循環就一直進行下去了。而這種情況在網絡擁塞的時候是可能發生的。

至於具體怎么修改就看業務邏輯應該怎么對待這種特殊情況了。

 

最后,總結下排查CPU故障的方法和技巧有哪些:

1、top命令:Linux命令。可以查看實時的CPU使用情況。也可以查看最近一段時間的CPU使用情況。

2、PS命令:Linux命令。強大的進程狀態監控命令。可以查看進程以及進程中線程的當前CPU使用情況。屬於當前狀態的采樣數據。

3、jstack:Java提供的命令。可以查看某個進程的當前線程棧運行情況。根據這個命令的輸出可以定位某個進程的所有線程的當前運行狀態、運行代碼,以及是否死鎖等等。

4、pstack:Linux命令。可以查看某個進程的當前線程棧運行情況。


免責聲明!

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



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