免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
深入理解JVM性能調(diào)優(yōu)

    在上文中我們分析了很多性能監(jiān)控工具,介紹這些工具的目的只有一個(gè),那就是找出對(duì)應(yīng)的性能瓶頸。盲目的性能調(diào)優(yōu)是沒有效果的,只有充分知道了哪里出了問題,針對(duì)性的結(jié)果才是立竿見影的。解決了主要的性能問題,那些次要的性能問題也就不足為慮了!


我們知道,性能問題無非就這么幾種:CPU、內(nèi)存、磁盤IO、網(wǎng)絡(luò)。那我們來逐一介紹以下相關(guān)的現(xiàn)象和一些可能出現(xiàn)的問題。


一、CPU過高。


查看CPU最簡(jiǎn)單的我們使用任務(wù)管理器查看,如下圖所示,windows下使用任務(wù)管理器查看,Linux下使用top查看。





一般我們的服務(wù)器都采用Linux,因此我們重點(diǎn)關(guān)注一下Linux(注:windows模式下相信大家已經(jīng)很熟悉了,并且前面我們已經(jīng)提到,使用資源監(jiān)視器可以很清楚的看到系統(tǒng)的各項(xiàng)參數(shù),在這里我就不多做介紹了)


top視圖下,對(duì)于多核的CPU,顯示的CPU資源有可能超過100%,因?yàn)檫@里顯示的是所有CPU占用百分百的總和,如果你需要看單個(gè)CPU的占用情況,直接按鍵1就可以看到。如下圖所示,我的一臺(tái)測(cè)試機(jī)為816GB內(nèi)存。




 在


top 視圖下,按鍵 shift+h 后,會(huì)顯示各個(gè)線程的 CPU 資源消耗情況,如下圖所示:



 我們也可以通過


sysstat 工具集的 pidstat 來查看

注:sysstat下載地址:http://sebastien.godard.pagesperso-orange.fr/download.html


安裝方法:


1、chmod +x configure


2./configure


3、make


4、make install



如輸入pidstat 1 2就會(huì)隔一秒在控制臺(tái)輸出一次當(dāng)然CPU的情況,共輸出2




 除了


top 、 pidstat 以外, vmstat 也可以進(jìn)行采樣分析



 相關(guān)


top 、 pidstat mstat 的用法大家可以去網(wǎng)上查找。

下面我們主要來介紹以下當(dāng)出現(xiàn)CPU過高的時(shí)候,或者CPU不正常的時(shí)候,我們?cè)撊绾稳ヌ幚恚?/span>


CPU消耗過高主要分為用戶進(jìn)程占用CPU過高和內(nèi)核進(jìn)程占用CPU過高(在Linuxtop視圖下us指的是用戶進(jìn)程,而sy是指內(nèi)核進(jìn)程),我們來看一個(gè)案例:




 程序運(yùn)行前,系統(tǒng)運(yùn)行平穩(wěn),其中藍(lán)色的線表示總的


CPU 利用率,而紅色的線條表示內(nèi)核使用率。部署 war 測(cè)試程序,運(yùn)行如下圖所示:



 對(duì)于一個(gè)


web 程序,還沒有任何請(qǐng)求就占用這么多 CPU 資源,顯然是不正常的。并且我們看到,不是系統(tǒng)內(nèi)核占用的大量 CPU ,而是系統(tǒng)進(jìn)程,那是哪一個(gè)進(jìn)程的呢?我們來看一下。



 很明顯是我們的


java 進(jìn)程,那是那個(gè)地方導(dǎo)致的呢?這就需要用到我們之前提到的性能監(jiān)控工具。在此我們使用可視化監(jiān)控工具 VisualVM 。



 首先我們排除了是


GC 過于頻繁而導(dǎo)致大 CPU 過高,因?yàn)楹苊黠@監(jiān)控視圖上沒有 GC 的活動(dòng)。然后我們打開 profilter 去查看以下,是那個(gè)線程導(dǎo)致了 CPU 的過高?



 前面一些線程都是容器使用的,而下面一個(gè)線程也一直在執(zhí)行,那是什么地方調(diào)用的呢?查找代碼中使用


ThredPoolExecutor 的地方。終于發(fā)現(xiàn)以下代碼。

    private BlockingQueue queue;


    private Executor executor;


//……


public void run() {


        while(true){


           try {


              SendMsg sendMsg = queue.poll();//從隊(duì)列中取出


              if(null != sendMsg) {


                  sendForQueue(sendMsg);


              }


           } catch (Exception e) {


              e.printStackTrace();


           }


       }



    }



問題很顯然了,我們看一下對(duì)應(yīng)BlockingQueuepoll方法的API文檔。




 不難理解了,雖然使用了阻塞的隊(duì)列,但是使用了非阻塞的取法,當(dāng)數(shù)據(jù)為空時(shí)直接返回


null ,那這個(gè)語句就等價(jià)于下面的語句。

@Override


    public void run() {


       while(true){


          


       }



    }



相當(dāng)于死循環(huán)么,很顯然是非常耗費(fèi)CPU資源的,并且我們還可以發(fā)現(xiàn)這樣的死循環(huán)是耗費(fèi)的單顆CPU資源,因此可以解釋上圖為啥有一顆CPU占用特別高。我們來看一下部署在Linux下的top視圖。




 猛一看,不是很高么?我們按鍵


1 來看每個(gè)單獨(dú) CPU 的情況!



 這下看的很清楚了吧!明顯一顆


CPU 被跑滿了。(因?yàn)橐粋€(gè)單獨(dú)的死循環(huán)只能用到一顆 CPU ,都是單線程運(yùn)行的)。

問題找到,馬上修復(fù)代碼為阻塞時(shí)存取,如下所示:



@Override


    public void run() {


       while(true){


           try {


              SendMsg sendMsg = queue.take();//從隊(duì)列中取出


              sendForQueue(sendMsg);


           } catch (Exception e) {


              e.printStackTrace();


           }


       }



    }





 再來監(jiān)控


CPU 的變換,我們可以看到,基本上不消耗 CPU 資源(是我沒做任何的訪問哦,有用戶建立線程就會(huì)消耗)。



 再來看


java 進(jìn)程的消耗,基本上不消耗 CPU 資源



 



再來看VisualVM的監(jiān)控,我們就可以看到基本上都是容器的一些線程了




 以上示例展示了


CPU 消耗過高情況下用戶線程占用特別高的情況。也就是 Linux top 視圖中 us 比較高的情況。發(fā)生這種情況的原因主要有以下幾種:程序不停的在執(zhí)行無阻塞的循環(huán)、正則或者純粹的數(shù)學(xué)運(yùn)算、 GC 特別頻繁。

CPU過高還有一種情況是內(nèi)核占用CPU很高。我們來看另外一個(gè)示例。



package com.yhj.jvm.monitor.cpu.sy;


 


import java.util.Random;


import java.util.concurrent.ExecutorService;


import java.util.concurrent.Executors;


 


/**


 * @Described:系統(tǒng)內(nèi)核占用CPU過高測(cè)試用例


 * @author YHJ create at 2012-3-28 下午05:27:33


 * @FileNmae com.yhj.jvm.monitor.cpu.sy.SY_Hign_TestCase.java


 */


public class SY_Hign_TestCase {


   


    private final static int LOCK_COUNT = 1000;


 


    //默認(rèn)初始化LOCK_COUNT個(gè)鎖對(duì)象


    private Object [] locks = new Object[LOCK_COUNT];


 


    private Random random = new Random();


 


    //構(gòu)造時(shí)初始化對(duì)應(yīng)的鎖對(duì)象


    public SY_Hign_TestCase() {


       for(int i=0;i<LOCK_COUNT;++i)


           locks[i]=new Object();


    }


 


 


 


    abstract class Task implements Runnable{


 


       protected Object lock;


 


       public Task(int index) {


           this.lock= locks[index];


       }


       @Override


       public void run() {


           while(true){  //循環(huán)執(zhí)行自己要做的事情


              doSth();


           }


       }


       //做類自己要做的事情


       public abstract void doSth();


    }


 


    //任務(wù)A 休眠自己的鎖


    class TaskA extends Task{


 


       public TaskA(int index) {


           super(index);


       }


 


       @Override


       public void doSth() {


           synchronized (lock) {


              try {


                  lock.wait(random.nextInt(10));


              } catch (InterruptedException e) {


                  e.printStackTrace();


              }


           }


       }


 


    }


 


    //任務(wù)B 喚醒所有鎖


    class TaskB extends Task{


      


       public TaskB(int index) {


           super(index);


        }


 


       @Override


       public void doSth() {


           try {


              synchronized (lock) {


                  lock.notifyAll();


                  Thread.sleep(random.nextInt(10));


              }


           } catch (InterruptedException e) {


              e.printStackTrace();


           }


       }


 


    }


    //啟動(dòng)函數(shù)


    public void start(){


       ExecutorService service = Executors.newCachedThreadPool();


       for(int i=0;i<LOCK_COUNT;++i){


           service.execute(new TaskA(i));


           service.execute(new TaskB(i));


       }


    }


    //主函數(shù)入口


    public static void main(String[] args) {


       new SY_Hign_TestCase().start();


    }


 



}



代碼很簡(jiǎn)單,就是創(chuàng)建了2000個(gè)線程,讓一定的線程去等待,另外一個(gè)線程去釋放這些資源,這樣就會(huì)有大量的線程切換,我們來看下效果。




 很明顯,


CPU 的內(nèi)核占用率很高,我們拿具體的資源監(jiān)視器看一下:



 很明顯可以看出有很多線程切換占用了大量的


CPU 資源。

同樣的程序部署在Linux下,top視圖如下圖所示:




 展開對(duì)應(yīng)的


CPU 資源,我們可以清晰的看到如下情形:



 大家可以看到有大量的


sy 內(nèi)核占用,但是也有不少的 us us 是因?yàn)槲覀儐⒂昧舜罅康难h(huán),而 sy 是因?yàn)榇罅烤€程切換導(dǎo)致的。

我們也可以使用vmstat來查看,如下圖所示:




 二、文件


IO 消耗過大,磁盤隊(duì)列高。

windows環(huán)境下,我們可以使用資源監(jiān)視器查看對(duì)應(yīng)的IO消耗,如下圖所示:




 這里不但可以看到當(dāng)前磁盤的負(fù)載信息,隊(duì)列詳情,還能看到每個(gè)單獨(dú)的進(jìn)程的資源消耗情況。


Linux下主要使用pidstat、iostat等進(jìn)行分析。如下圖所示


Pidstat –d –t –p [pid] {time} {count}


如:pidstat -d -t -p 18720 1 1





Iostat



 

Iostat –x xvda 1 10做定時(shí)采樣




 廢話不多說,直接來示例,上干貨!



package com.yhj.jvm.monitor.io;


 


import java.io.BufferedWriter;


import java.io.FileWriter;


import java.io.IOException;


import java.util.concurrent.ExecutorService;


import java.util.concurrent.Executors;


 


/**


 * @DescribedIO測(cè)試用例


 * @author YHJ create at 2012-3-29 上午09:56:06


 * @FileNmae com.yhj.jvm.monitor.io.IO_TestCase.java


 */


public class IO_TestCase {


   


    private String fileNmae = "monitor.log";


   


    private String context ;


   


    // CPU處理器個(gè)數(shù)相同,既充分利用CPU資源,又導(dǎo)致線程頻繁切換


    private final static int THRED_COUNT = Runtime.getRuntime().availableProcessors();


   


    public IO_TestCase() {//加長寫文件的內(nèi)容,拉長每次寫入的時(shí)間


       StringBuilder sb = new StringBuilder();


       for(int i=0;i<1000;++i){


           sb.append("context index :")


           .append(i)


           .append("\n");


           this.context= new String(sb);


       }


    }


    //寫文件任務(wù)


    class Task implements Runnable{


 


       @Override


       public void run() {


           while(true){


              BufferedWriter writer = null;


              try {


                  writer = new BufferedWriter(new FileWriter(fileNmae,true));//追加模式


                  writer.write(context);


              } catch (Exception e) {


                  e.printStackTrace();


              }finally{


                  try {


                     writer.close();


                  } catch (IOException e) {


                     e.printStackTrace();


                  }


              }


           }


          


       }


    }


    //啟動(dòng)函數(shù)


    public void start(){


       ExecutorService service = Executors.newCachedThreadPool();


       for(int i=0;i<THRED_COUNT;++i)


           service.execute(new Task());


    }


    //主函數(shù)入口


    public static void main(String[] args) {


       new IO_TestCase().start();


    }


 



}



這段示例很簡(jiǎn)單,通過創(chuàng)建一個(gè)和CPU個(gè)數(shù)相同的線程池,然后開啟這么多線程一起讀寫同一個(gè)文件,這樣就會(huì)因IO資源的競(jìng)爭(zhēng)而導(dǎo)致IO的隊(duì)列很高,如下圖所示:




 關(guān)掉之后馬上就下來了




 我們把這個(gè)部署到


Linux 上觀看。



 這里的


%idle 指的是系統(tǒng)沒有完成寫入的數(shù)量占用 IO 總量的百分百,為什么這么高我們的系統(tǒng)還能承受?因?yàn)槲疫@臺(tái)機(jī)器的內(nèi)存為 16GB 的,我們來查看以下 top 視圖就可以清晰的看到。



 占用了大量的內(nèi)存資源。


三、內(nèi)存消耗


對(duì)于JVM的內(nèi)存模型大家已經(jīng)很清楚了,前面我們講了JVM的性能監(jiān)控工具。對(duì)于Java應(yīng)用來說,出現(xiàn)問題主要消耗在于JVM的內(nèi)存上,而JVM的內(nèi)存,JDK已經(jīng)給我們提供了很多的工具。在實(shí)際的生成環(huán)境,大部分應(yīng)用會(huì)將-Xms-Xmx設(shè)置為相同的,避免運(yùn)行期間不斷開辟內(nèi)存。


對(duì)于內(nèi)存消耗,還有一部分是直接物理內(nèi)存的,不在堆空間,前面我們也寫過對(duì)應(yīng)的示例。之前一個(gè)系統(tǒng)就是因?yàn)橛写罅康?/span>NIO操作,而NIO是使用物理內(nèi)存的,并且開辟的物理內(nèi)存是在觸發(fā)FULL GC的時(shí)候才進(jìn)行回收的,但是當(dāng)時(shí)的機(jī)器總內(nèi)存為16GB 給堆的內(nèi)存是14GB Edon1.5GB,也就是實(shí)際剩下給物理呢哦村的只有0.5GB,最終導(dǎo)致總是發(fā)生內(nèi)存溢出,但監(jiān)控堆、棧的內(nèi)存消耗都不大。在這里我就不多寫了!


四、網(wǎng)絡(luò)消耗過大


Windows下使用本地網(wǎng)絡(luò)視圖可以監(jiān)控當(dāng)前的網(wǎng)絡(luò)流量大小




 更詳細(xì)的資料可以打開資源監(jiān)視器,如下圖所示




 Linux


平臺(tái)可以使用以下 sar 命令查看

sar -n DEV 1 2




 字段說明:


rxpck/s:每秒鐘接收的數(shù)據(jù)包


txpck/s:每秒鐘發(fā)送的數(shù)據(jù)包


rxbyt/s:每秒鐘接收的字節(jié)數(shù)


txbyt/s:每秒鐘發(fā)送的字節(jié)數(shù)


rxcmp/s:每秒鐘接收的壓縮數(shù)據(jù)包


txcmp/s:每秒鐘發(fā)送的壓縮數(shù)據(jù)包


rxmcst/s:每秒鐘接收的多播數(shù)據(jù)包


Java程序一般不會(huì)出現(xiàn)網(wǎng)絡(luò)IO導(dǎo)致問題,因此在這里也不過的的闡述。


五、程序執(zhí)行緩慢


當(dāng)CPU、內(nèi)存、磁盤、網(wǎng)絡(luò)都不高,程序還是執(zhí)行緩慢的話,可能引發(fā)的原因大致有以下幾種:


1程序鎖競(jìng)爭(zhēng)過于激烈,比如你只有2CPU,但是你啟用了200個(gè)線程,就會(huì)導(dǎo)致大量的線程等待和切換,而這不會(huì)導(dǎo)致CPU很高,但是很多線程等待意味著你的程序運(yùn)行很慢。


2未充分利用硬件資源。比如你的機(jī)器是16個(gè)核心的,但是你的程序是單線程運(yùn)行的,即使你的程序優(yōu)化的很好,當(dāng)需要處理的資源比較多的時(shí)候,程序還會(huì)很慢,因此現(xiàn)在都在提倡分布式,通過大量廉價(jià)的PC機(jī)來提升程序的執(zhí)行速度!


3其他服務(wù)器反應(yīng)緩慢,如數(shù)據(jù)庫、緩存等。當(dāng)大量做了分布式,程序CPU負(fù)載都很低,但是提交給數(shù)據(jù)庫的sql無法很快執(zhí)行,也會(huì)特別慢。


總結(jié)一下,當(dāng)出現(xiàn)性能問題的時(shí)候我們?cè)撛趺醋觯?/span>


一、CPU過高


1、  us過高


使用監(jiān)控工具快讀定位哪里有死循環(huán),大計(jì)算,對(duì)于死循環(huán)通過阻塞式隊(duì)列解決,對(duì)于大計(jì)算,建議分配單獨(dú)的機(jī)器做后臺(tái)計(jì)算,盡量不要影響用戶交互,如果一定要的話(如框計(jì)算、云計(jì)算),只能通過大量分布式來實(shí)現(xiàn)


2、  sy過高


最有效的方法就是減少進(jìn)程,不是進(jìn)程越多效率越高,一般來說線程數(shù)和CPU的核心數(shù)相同,這樣既不會(huì)造成線程切換,又不會(huì)浪費(fèi)CPU資源


二、內(nèi)存消耗過高


1、  及時(shí)釋放不必要的對(duì)象


2、  使用對(duì)象緩存池緩沖


3、  采用合理的緩存失效算法(還記得我們之前提到的弱引用、幽靈引用么?)


三、磁盤IO過高


1、  異步讀寫文件


2、  批量讀寫文件


3、  使用緩存技術(shù)


4、  采用合理的文件讀寫規(guī)則


四、網(wǎng)絡(luò)


1、增加寬帶流量


五、資源消耗不多但程序運(yùn)行緩慢


1、使用并發(fā)包,減少鎖競(jìng)爭(zhēng)


2、對(duì)于必須單線程執(zhí)行的使用隊(duì)列處理


3、大量分布式處理


六、未充分利用硬件資源


1、  修改程序代碼,使用多線程處理


2、  修正外部資源瓶頸,做業(yè)務(wù)拆分


3、  使用緩存




轉(zhuǎn)自:http://yhjhappy234.blog.163.com/blog/static/3163283220122298232721/?suggestedreading&wumii


本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Java多線程:volatile變量、happens
volatile可見性的一些認(rèn)識(shí)和論證
文件IO消耗嚴(yán)重的問題
構(gòu)建可擴(kuò)展的Java EE應(yīng)用(一) BlueDavy
重啟大法好!線上常見問題排查手冊(cè)
Elasticsearch 生產(chǎn)環(huán)境集群部署最佳實(shí)踐
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服