NO IMAGE


源自部落格園:
http://www.cnblogs.com/duanxz/p/3712734.html

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

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

在windows作業系統下,當我們的系統執行很慢的時候,80%的人首先檢視的就是工作管理員,因為它可以讓我們快讀的知道是那個程式佔用了較多的資源(如CPU、記憶體、磁碟IO等),或者是那個程序不能響應導致整個作業系統巨卡,我們通過工作管理員可以輕鬆的檢視和管理我們的應用程式,如下圖所示:

 

 

 

而從windowsVista核心以後(win7、win8、win2008等OS),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都為我們提供了哪些效能檢測工具。

一、Jps(JVM 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的啟動引數

 

 二、

jstat(JVM Statistics Monitoring Tools)

Jstat主要用於監控虛擬機器的各種執行狀態資訊,如類的裝載、記憶體、垃圾回收、JIT編譯器等,在沒有GUI的伺服器上,這款工具是首選的一款監控工具。其用法如下:

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

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

jstat –gc 20445 1 20

 

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

官方的說明(注:網址連結請點選此處

選項option代表使用者需要查詢的虛擬機器的資訊,主要分為3類:類裝載、垃圾回收和執行期的編譯情況,具體如下表所示:

Option

Function

-class

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

-gc

監視Java堆,包含eden、2個survivor區、old區和永久帶區域的容量、已用空間、GC時間合計等資訊

-gccapcity

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

-gcutil

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

-gccause

與-gcutil輸出資訊相同,額外輸出導致上次GC產生的原因

-gcnew

監控新生代的GC情況

-gcnewcapacity

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

-gcold

監控老生代的GC情況

-gcoldcapacity

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

-gcpermcapacity

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

-compiler

輸出JIT編譯器編譯過的方法、耗時資訊

-printcompilation

輸出已經被JIT編譯的方法

三、jinfo(JVM configuration Info for Java)

Jinfo的作用是實時檢視虛擬機器的各項引數資訊jps –v可以檢視虛擬機器在啟動時被顯式指定的引數資訊,但是如果你想知道預設的一些引數資訊呢?除了去查詢對應的資料以外,jinfo就顯得很重要了。jinfo的用法如下:

Jinfo [option] pid

如 jinfo –sysprops {pid}

 …

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

四、jmap(JVM 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

 

 五、

jhat(JVM 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

 

 //……..

 

 然後我們啟動瀏覽器檢視

 我們可以看到,很詳細的類資訊都被抓了出來

 

 六、

jstack(JVM Stack Trace for java)

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

Jstack [option] vmid

相關的option和function如下表所示

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。下面我們來逐一看一下!

一、JConsole(JVM Monitoring and management console)

在JDK的bin目錄下,我們很容易找到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]之間的數字會被快取掉,也就是我們迴圈中傳輸了那麼多的1和2其實就返回的2個,當某個物件持有1,而另外一個物件持有2的時候,第一個等待2的釋放,而第二個等待1的釋放,因此就發生了死鎖。其實這個程式的迴圈也是不需要的,2個執行緒就可能引發死鎖,但是程式執行太快,概率太小,加一個1000次的迴圈就是為了增大這種概率。

二、VisualVM

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

1、  顯示虛擬機器的程序以及程序的配置資訊和環境資訊(jps、jinfo)

2、  監視應用程式的CPU、記憶體、堆、方法區和執行緒資訊(jstat、jstack)

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

4、  離執行緒序快照:離線dump分析

5、  方法執行效能分析,找出呼叫最多,執行最長的方法塊

6、  Plugings動態擴充套件功能

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

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

 

 VisualVM

的外掛安裝圖示如下圖所示:

 

 

 

 

 

 

 

 

 

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