經(jīng)驗(yàn)不在于年限,在于積累---專注互聯(lián)網(wǎng)軟件開發(fā)

          把工作當(dāng)事業(yè)做,把項(xiàng)目當(dāng)作品做!

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            55 Posts :: 0 Stories :: 66 Comments :: 0 Trackbacks

          一個(gè)應(yīng)用占用CPU很高,除了確實(shí)是計(jì)算密集型應(yīng)用之外,通常原因都是出現(xiàn)了死循環(huán)。

          (友情提示:本博文章歡迎轉(zhuǎn)載,但請(qǐng)注明出處:hankchen,http://www.aygfsteel.com/hankchen

          以我們最近出現(xiàn)的一個(gè)實(shí)際故障為例,介紹怎么定位和解決這類問題。

          clip_image002

          根據(jù)top命令,發(fā)現(xiàn)PID為28555的Java進(jìn)程占用CPU高達(dá)200%,出現(xiàn)故障。

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

          首先顯示線程列表:

          ps -mp pid -o THREAD,tid,time

          1

          找到了耗時(shí)最高的線程28802,占用CPU時(shí)間快兩個(gè)小時(shí)了!

          其次將需要的線程ID轉(zhuǎn)換為16進(jìn)制格式:

          printf "%x\n" tid

          2

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

          jstack pid |grep tid -A 30

          3

          找到出現(xiàn)問題的代碼了!

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

          ShortSocketIO是應(yīng)用封裝的一個(gè)用短連接Socket通信的工具類。readBytes函數(shù)的代碼如下:

          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;

                  }

             }

          }

          問題就出在標(biāo)紅的代碼部分。如果this.in.read()返回的數(shù)據(jù)小于等于0時(shí),循環(huán)就一直進(jìn)行下去了。而這種情況在網(wǎng)絡(luò)擁塞的時(shí)候是可能發(fā)生的。

          至于具體怎么修改就看業(yè)務(wù)邏輯應(yīng)該怎么對(duì)待這種特殊情況了。

           

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

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

          2、PS命令:Linux命令。強(qiáng)大的進(jìn)程狀態(tài)監(jiān)控命令。可以查看進(jìn)程以及進(jìn)程中線程的當(dāng)前CPU使用情況。屬于當(dāng)前狀態(tài)的采樣數(shù)據(jù)。

          3、jstack:Java提供的命令。可以查看某個(gè)進(jìn)程的當(dāng)前線程棧運(yùn)行情況。根據(jù)這個(gè)命令的輸出可以定位某個(gè)進(jìn)程的所有線程的當(dāng)前運(yùn)行狀態(tài)、運(yùn)行代碼,以及是否死鎖等等。

          4、pstack:Linux命令。可以查看某個(gè)進(jìn)程的當(dāng)前線程棧運(yùn)行情況。

          (友情提示:本博文章歡迎轉(zhuǎn)載,但請(qǐng)注明出處:hankchen,http://www.aygfsteel.com/hankchen

          posted on 2012-05-09 20:20 hankchen 閱讀(41734) 評(píng)論(7)  編輯  收藏 所屬分類: 工作感悟

          Feedback

          # 非常感謝![未登錄] 2012-08-09 10:05 lk
          最近我們的應(yīng)用極不正常,CPU都占到700%了,用你的方法查了一下,終于找到了原因!非常感謝!  回復(fù)  更多評(píng)論
            

          # re: 線上應(yīng)用故障排查之一:高CPU占用 2013-03-01 17:11 netcat
          您好,請(qǐng)教個(gè)問題:如何查看tomcat連接池的排隊(duì)請(qǐng)求數(shù)。我email:297020555@qq.com,多謝。  回復(fù)  更多評(píng)論
            

          # re: 線上應(yīng)用故障排查之一:高CPU占用 2013-03-13 14:38 boys99@163.com
          非常謝謝你,用你的方法幫我快速定位到問題所在點(diǎn)。謝謝!  回復(fù)  更多評(píng)論
            

          # re: 線上應(yīng)用故障排查之一:高CPU占用 2014-08-18 17:27 少林功夫好
          如果不是java進(jìn)程呢?如果是php-fpm,是nginx怎么辦呢?新人,求一些指導(dǎo)。  回復(fù)  更多評(píng)論
            

          # re: 線上應(yīng)用故障排查之一:高CPU占用 2015-06-25 12:49 Jerry Lee
          腳本show-busy-java-threads.sh https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#beer-show-busy-java-threadssh 可以一鍵找出出在運(yùn)行的Java進(jìn)程中,消耗CPU最多的線程棧。用于快速排查Java的性能問題。 可以試試 :)
            回復(fù)  更多評(píng)論
            

          # re: 線上應(yīng)用故障排查之一:高CPU占用 2015-08-06 16:41 王亮
          很感謝您的這篇文章,謝謝  回復(fù)  更多評(píng)論
            

          # re: 線上應(yīng)用故障排查之一:高CPU占用[未登錄] 2016-03-09 18:09 QQ
          嗯 太贊了  回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 盐源县| 东台市| 六安市| 安宁市| 图们市| 韩城市| 西吉县| 广昌县| 资溪县| 宁德市| 灵武市| 太谷县| 五家渠市| 宁津县| 巴林右旗| 江山市| 保定市| 萝北县| 凌源市| 新乐市| 贵南县| 南漳县| 凌海市| 襄城县| 布尔津县| 枣庄市| 乌兰察布市| 静安区| 那曲县| 安溪县| 峡江县| 璧山县| 金秀| 抚松县| 乌拉特前旗| 宁化县| 铜梁县| 汤阴县| 东至县| 琼中| 高台县|