jvm性能調優 之 工具說明

我們知道,在JVM編譯期和加載器,甚至運行期已經做了大量的調優操作,但是那些都是JVM針對Java程序所做的通用的、簡單的優化,程序在運行時由於運行環境的複雜性、業務邏輯的複雜性,很多JVM是無法進行優化處理的,這就需要我們自己在寫代碼的時候就注意,以便我們的程序在特定的業務場景發揮到最佳性能。

要進行性能調優,首先我們要找到程序的性能瓶頸在哪裏?而要知道性能瓶頸在哪裏,我們需要藉助一定的工具進行處理。

windows操作系統下,當我們的系統運行很慢的時候,80%的人首先查看的就是任務管理器,因爲它可以讓我們快讀的知道是那個程序佔用了較多的資源(如CPU、內存、磁盤IO等),或者是那個進程不能響應導致整個操作系統巨卡,我們通過任務管理器可以輕鬆的查看和管理我們的應用程序,如下圖所示:





而從windowsVista內核以後(win7win8win2008OS),windows操作系統還提供了更爲詳細的一個資源監視器,它可以讓我們更清晰的知道更多的細節信息,在資源管理器上有一個資源監視器的入口,如下圖所示



 打開對應的資源監視器,如下圖所示:


概述

tab頁面我們可以大致的看到對應的進程信息,哪些進程佔用了多少的資源等信息,而對應的CPU、內存、磁盤、網絡則更詳細的展示了具體的信息,如下圖所示,磁盤的信息,哪些進程在操作哪些個文件,磁盤的隊列有多少?都一目瞭然。



以上是

windows自帶的一些監控工具,當然我們知道面向windows的監控工具比比皆是,我在這裏就不多說了。

而我們的服務器大部分是運行在Linux下,如我們現在的服務器使用的是CentOS5.5的操作系統(Linux2.6內核),那在Linux下都有哪些工具可以使用的呢?

Linux下使用的最頻繁的一個命令是top,如下圖所示




這個就相當於

windows下的任務管理器,能夠簡單的描述每個進程佔用的資源信息,包含CPU、磁盤、內存等信息,按1可以將CPU拆解,看單個CPU的運行信息。使用ps –ef | grep 進程名可以查看對應進程的簡單信息,如ps –ef|grep java,如下圖所示:


 當然,針對我們

Java開發人員,這一點是遠遠不夠的,我們還需要更詳細的信息。Linux工具集SysStat(下載地址:http://sebastien.godard.pagesperso-orange.fr/download.html)爲我們提供了一系列指令集,我們在尋找下面會提到,但其實JDK已經爲我們提供了很多很好的針對Java的性能監控工具,下面我們就來一起看一下JDK都爲我們提供了哪些性能檢測工具。

一、JpsJVM Process Status Tools

Jps是參照Unix系統的取名規則命名的,而他的功能和ps的功能類似,可以列舉正在運行的餓虛擬機進程並顯示虛擬機執行的主類以及這些進程的唯一ID(LVMID,對應本機來說和PID相同),他的用法如下:

Jps [option] [hostid]

其中hostid默認爲本機,而option選項包含以下選項

Option

Function

-q

只輸出LVMID

-m

輸出JVM啓動時傳給主類的方法

-l

輸出主類的全名,如果是Jar則輸出jar的路徑

-v

輸出JVM的啓動參數





二、

jstatJVM Statistics Monitoring Tools

Jstat主要用於監控虛擬機的各種運行狀態信息,如類的裝載、內存、垃圾回收、JIT編譯器等,在沒有GUI的服務器上,這款工具是首選的一款監控工具。其用法如下:

jstat [option vmid [interval [s|ms] [vount] ] ]

參數intervalcount分別表示查詢間隔和查詢次數,如每1毫秒查詢一次進程20445的垃圾回收情況,監控20次,命令如下所示:

jstat –gc 20445 1 20



相關的輸出參數介紹可參照

官方的說明(注:網址鏈接請點擊此處

選項option代表用戶需要查詢的虛擬機的信息,主要分爲3類:類裝載、垃圾回收和運行期的編譯情況,具體如下表所示:

Option

Function

-class

監視類的裝載、卸載數量以及類的裝載總空間和耗費時間等

-gc

監視Java堆,包含eden2survivor區、old區和永久帶區域的容量、已用空間、GC時間合計等信息

-gccapcity

監視內容與-gc相同,但輸出主要關注Java區域用到的最大和最小空間

-gcutil

監視內容與-gc相同,但輸出主要關注已使用空間佔總空間的百分比

-gccause

-gcutil輸出信息相同,額外輸出導致上次GC產生的原因

-gcnew

監控新生代的GC情況

-gcnewcapacity

-gcnew監控信息相同,輸出主要關注使用到的最大和最小空間

-gcold

監控老生代的GC情況

-gcoldcapacity

-gcold監控信息相同,輸出主要關注使用到的最大和最小空間

-gcpermcapacity

輸出永久帶用到的最大和最小空間

-compiler

輸出JIT編譯器編譯過的方法、耗時信息

-printcompilation

輸出已經被JIT編譯的方法

三、jinfoJVM configuration Info for Java

Jinfo的作用是實時查看虛擬機的各項參數信息jps –v可以查看虛擬機在啓動時被顯式指定的參數信息,但是如果你想知道默認的一些參數信息呢?除了去查詢對應的資料以外,jinfo就顯得很重要了。jinfo的用法如下:

Jinfo [option] pid

 jinfo –sysprops {pid}




還有很多參數信息,我們可以看到除了我們顯式指定的參數信息以外,JVM的默認參數一覽無餘。同時,從JDK1.6以後,jinfo加入了運行時修改參數信息的能力,可以使用-flag [+|-]name 或者-flag name=value來修改一部分運行期可以寫入的虛擬機參數。更多信息可參考官方文檔

四、jmapJVM Memory Map for Java

Jmap用於生成堆快照(heapdump)。當然我們有很多方法可以取到對應的dump信息,如我們通過JVM啓動時加入啓動參數 –XX:HeapDumpOnOutOfMemoryError參數,可以讓JVM在出現內存溢出錯誤的時候自動生成dump文件,亦可以通過-XX:HeapDumpOnCtrlBreak參數,在運行時使用ctrl+break按鍵生成dump文件,當然我們也可以使用kill -3 pid的方式去恐嚇JVM生成dump文件。Jmap的作用不僅僅是爲了獲取dump文件,還可以用於查詢finalize執行隊列、Java堆和永久帶的詳細信息,如空間使用率、垃圾回收器等。其運行格式如下:

Jmap [option] vmip

Option的信息如下表所示

Option

Function

-dump

生成對應的dump信息,用法爲-dump:[live,]format=b,file={fileName}

-finalizerinfo

顯示在F-Queue中等待的Finalizer方法的對象(只在linux下生效)

-heap

顯示堆的詳細信息、垃圾回收器信息、參數配置、分代詳情等

-histo

顯示堆棧中的對象的統計信息,包含類、實例數量和合計容量

-permstat

ClassLoder爲統計口徑顯示永久帶的內存狀態

-F

當虛擬機對-dump無響應時可使用這個選項強制生成dump快照

示例:jmap -dump:format=b,file=yhj.dump 20445




 五、

jhatJVM Heap Analysis Tool

Jhat是用來分析dump文件的一個微型的HTTP/HTML服務器,它能將生成的dump文件生成在線的HTML文件,讓我們可以通過瀏覽器進行查閱,然而實際中我們很少使用這個工具,因爲一般服務器上設置的堆、棧內存都比較大,生成的dump也比較大,直接用jhat容易造成內存溢出,而是我們大部分會將對應的文件拷貝下來,通過其他可視化的工具進行分析。啓用法如下:

Jhat {dump_file}

執行命令後,我們看到系統開始讀取這段dump信息,當系統提示Server is ready的時候,用戶可以通過在瀏覽器鍵入http://ip地址:7000進行查詢。

我們可以看到剛纔生成的dump文件有多大


我們來生成以下看看!

jhat yhj.dump




//……..



 然後我們啓動瀏覽器查看


們可以看到,很詳細的類信息都被抓了出來



六、

jstackJVM Stack Trace for java

Jstack用於JVM當前時刻的線程快照,又稱threaddump文件,它是JVM當前每一條線程正在執行的堆棧信息的集合。生成線程快照的主要目的是爲了定位線程出現長時間停頓的原因,如線程死鎖、死循環、請求外部時長過長導致線程停頓的原因。通過jstack我們就可以知道哪些進程在後臺做些什麼?在等待什麼資源等!其運行格式如下:

Jstack [option] vmid

相關的optionfunction如下表所示

Option

Function

-F

當正常輸出的請求不響應時強制輸出線程堆棧

-l

除堆棧信息外,顯示關於鎖的附加信息

-m

顯示native方法的堆棧信息

示例:jstack -l 20445


JDK1.5以後,java.lang.Thread類增加了一個getAllStackTraces()方法用於獲取虛擬機中的StackTraceElement對象,使用這段代碼我們可以通過很簡單的代碼獲取對應JVM的信息,下面是一個簡單的示例:

package com.yhj.monitor;

 

import java.util.Map;

import java.util.Set;

 

/**

 * @Described:線程監控器

 * @author YHJ create at 2012-3-26 下午05:20:18

 * @FileNmae com.yhj.monitor.Threadmonitor.java

 */

public class Threadmonitor {

 

    public static void main(String[] args) {

       Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();

       Set<Thread> set = map.keySet();

       for(Thread thread : set){

           System.out.println("檢測到線程["+thread.getId()+":"+thread.getName()+"],線程詳細信息:");

           for(StackTraceElement trace:map.get(thread)){

              System.out.println(trace);

           }

       }

    }

 

}

一次運行結果如下:




如果我們把這個寫入一個

web工程的一個監控頁面上,一個小的監控線程的程序就有了, 

介紹了前面幾個命令,大家也許還在擔心如何記住這麼多詳細的參數,其實JDK爲我們提供了更爲強大的GUI的監控工具,囊括了上面所有的監控工具的功能,同時還增加了很多工具方便我們的故障分析與性能監控!

JDK1.5開始,JDK加入了可視化監控工具Jconsole,而從JDK1.6u7以後加了了多功能於一體的可視化監控工具VisualVM。下面我們來逐一看一下!

一、JConsoleJVM Monitoring and management console

JDKbin目錄下,我們很容易找到jconsole.exe這個程序,雙擊即可啓動!




這款工具既可以實現本地監控,亦可以實現遠程監控

.

啓動後界面如圖所示:




 我們可以清楚的查看對應的

CPu、內存、類和一起其他的詳細信息。

在內存的tab頁面,我們可以看到內存的變化。




在線程

tab,我們可以追蹤對應線程的變化情況



我們來監控一段代碼:

package com.yhj.monitor;

/**

 * @Described:死鎖演示

 * @author YHJ create at 2012-3-26 下午05:46:36

 * @FileNmae com.yhj.monitor.Deadlock.java

 */

public class Deadlock implements Runnable{

 

    private int a;

    private int b;

   

    public Deadlock(int a, int b) {

       super();

       this.a = a;

       this.b = b;

    }

 

    @Override

    public void run() {

       synchronized (Integer.valueOf(a)) {

           synchronized (Integer.valueOf(b)) {

              System.out.println("a+b="+(a+b));

           }

       }

    }

   

    public static void main(String[] args) {

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

           new Thread(new Deadlock(1, 2)).start();

           new Thread(new Deadlock(2, 1)).start();

       }

    }

}

這段代碼執行一段時間你會發現他不動了,如下圖所示:





我們使用

Jconsole的線程tab,下面有一個檢測死鎖的按鈕,點擊一下





Jconsole很清楚的告訴我們發生了死鎖,如上圖所示。並且明確的告訴我們死鎖線程每個線程都在幹什麼?什麼資源導致了死鎖的發生,很精確。

很多人還在迷惑,上段代碼怎麼可能發生死鎖的?每個線程獨享一個a和一個b,互不相干,怎麼可能發生死鎖呢?

其實發生死鎖的原因是我們調用了Integer.valueOf()方法,這個方法是基於減少創建對象次數和節省內存設計的,出於這個的考慮,在[-128~127]之間的數字會被緩存掉,也就是我們循環中傳輸了那麼多的12其實就返回的2個,當某個對象持有1,而另外一個對象持有2的時候,第一個等待2的釋放,而第二個等待1的釋放,因此就發生了死鎖。其實這個程序的循環也是不需要的,2個線程就可能引發死鎖,但是程序執行太快,概率太小,加一個1000次的循環就是爲了增大這種概率。

二、VisualVM

VisualVM被成爲是more in one的工具集,它可以實現以下功能點:

1、  顯示虛擬機的進程以及進程的配置信息和環境信息(jpsjinfo)

2、  監視應用程序的CPU、內存、堆、方法區和線程信息(jstatjstack)

3、  Dump以及分析dump的功能(jmapjhat)

4、  離線程序快照:離線dump分析

5、  方法運行性能分析,找出調用最多,運行最長的方法塊

6、  Plugings動態擴展功能

VisualVM因爲是基於netBean開發,因此天生就具有plug大量擴展的能力,我們可以通過他的插件頁面輕鬆安裝所需要的插件!

啓用VisualVM工具會很醒目的告訴我們檢測到一個死鎖,而不需要我們在Jconsole下手動啓用檢測。如下圖所示:




VisualVM

的插件安裝圖示如下圖所示:











VisualVM中有一個開源的,強大的插件,叫做BTrace。這是一個很有意思的插件,假設這麼一種場景,某天你的程序突然出現了某個差錯,但是有沒有寫日誌,怎麼辦呢?BTrace就可以通過簡單的代碼注入,在你不重啓服務器的情況下動態加入相關的日誌語句。相關示例請參見官方DEMOhttps://hg.kenai.com/hg/btrace~hg/file/d31d25ebd48b/samples






發佈了123 篇原創文章 · 獲贊 15 · 訪問量 58萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章