Java性能調優工具

一、JDK命令行工具                                                                       html

1.一、jps命令java

      jps用於列出Java的進程,jps能夠增長參數,-m用於輸出傳遞給Java進程的參數,-l用於輸出主函數的完整路徑,-v能夠用於顯示傳遞給jvm的參數。正則表達式

jps -l -m -v
31427 sun.tools.jps.Jps -l -m -v -Dapplication.home=/Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home -Xms8m

1.二、jstat命令瀏覽器

      jstat是一個能夠用於觀察Java應用程序運行時信息的工具,它的功能很是強大,能夠經過它查看堆信息的詳細狀況,它的基本使用方法爲:性能優化

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]] 

  選項option能夠由如下值組成:服務器

    jstat -class pid:顯示加載class的數量,及所佔空間等信息。  
    jstat -compiler pid:顯示VM實時編譯的數量等信息。  
    jstat -gc pid:能夠顯示gc的信息,查看gc的次數,及時間。其中最後五項,分別是young gc的次數,young gc的時間,full gc的次數,full gc的時間,gc的總時間。  
    jstat -gccapacity:能夠顯示,VM內存中三代(young,old,perm)對象的使用和佔用大小,如:PGCMN顯示的是最小perm的內存使用量,PGCMX顯示的是perm的內存最大使用量,PGC是當前新生成的perm內存佔用量,PC是但前perm內存佔用量。其餘的能夠根據這個類推, OC是old內純的佔用量。  
    jstat -gcnew pid:new對象的信息。  
    jstat -gcnewcapacity pid:new對象的信息及其佔用量。  
    jstat -gcold pid:old對象的信息。  
    jstat -gcoldcapacity pid:old對象的信息及其佔用量。  
    jstat -gcpermcapacity pid: perm對象的信息及其佔用量。  
    jstat -gcutil pid:統計gc信息統計。  
    jstat -printcompilation pid:當前VM執行的信息。  
    除了以上一個參數外,還能夠同時加上 兩個數字,如:jstat -printcompilation 3024 250 6是每250毫秒打印一次,一共打印6次。

      這些參數中最經常使用的參數是gcutil,下面是該參數的輸出介紹以及一個簡單例子:  oracle

S0  — Heap上的 Survivor space 0 區已使用空間的百分比  
S1  — Heap上的 Survivor space 1 區已使用空間的百分比  
E   — Heap上的 Eden space 區已使用空間的百分比  
O   — Heap上的 Old space 區已使用空間的百分比  
P   — Perm space 區已使用空間的百分比  
YGC — 從應用程序啓動到採樣時發生 Young GC 的次數  
YGCT– 從應用程序啓動到採樣時 Young GC 所用的時間(單位秒)  
FGC — 從應用程序啓動到採樣時發生 Full GC 的次數  
FGCT– 從應用程序啓動到採樣時 Full GC 所用的時間(單位秒)  
GCT — 從應用程序啓動到採樣時用於垃圾回收的總時間(單位秒)  
  
實例使用1:  
  
[root@localhost bin]# jstat -gcutil 25444  
  
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT  
  
 11.63   0.00   56.46  66.92  98.49 162    0.248    6      0.331    0.579  

1.三、jinfo命令app

      jinfo能夠用來查看正在運行的Java應用程序的擴展參數,甚至在運行時修改部分參數,它的基本語法爲:dom

jinfo  <option>  <pid>

  jinfo能夠查看運行時參數:eclipse

jinfo -flag MaxTenuringThreshold 31518
-XX:MaxTenuringThreshold=15

  jinfo還能夠在運行時修改參數值:

> jinfo -flag PrintGCDetails 31518
-XX:-PrintGCDetails
> jinfo -flag +PrintGCDetails 31518
> jinfo -flag PrintGCDetails 31518
-XX:+PrintGCDetails

1.四、jmap命令

      jmap命令主要用於生成堆快照文件,它的使用方法以下:

> jmap -dump:format=b,file=heap.hprof 31531
Dumping heap to /Users/caojie/heap.hprof ...
Heap dump file created

  得到堆快照文件以後,咱們可使用多種工具對文件進行分析,例如jhat,visual vm等。

1.五、jhat命令

      使用jhat工具能夠分析Java應用程序的堆快照文件,使用命令以下:

> jhat heap.hprof 
Reading from heap.hprof...
Dump file created Tue Nov 11 06:02:05 CST 2014
Snapshot read, resolving...
Resolving 8781 objects...
Chasing references, expect 1 dots.
Eliminating duplicate references.
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

  jhat在分析完成以後,使用HTTP服務器展現其分析結果,在瀏覽器中訪問http://127.0.0.1:7000/便可獲得分析結果。

1.六、jstack命令

  jstack可用於導出Java應用程序的線程堆棧信息,語法爲:

jstack -l <pid>

  jstack能夠檢測死鎖,下例經過一個簡單例子演示jstack檢測死鎖的功能。java代碼以下:

public class DeadLock extends Thread {
	protected Object myDirect;
	static ReentrantLock south = new ReentrantLock();
	static ReentrantLock north = new ReentrantLock();

	public DeadLock(Object obj) {
		this.myDirect = obj;
		if (myDirect == south) {
			this.setName("south");
		}
		if (myDirect == north) {
			this.setName("north");
		}
	}

	@Override
	public void run() {
		if (myDirect == south) {
			try {
				north.lockInterruptibly();
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				south.lockInterruptibly();
				System.out.println("car to south has passed");
			} catch (InterruptedException e1) {
				System.out.println("car to south is killed");
			} finally {
				if (north.isHeldByCurrentThread())
					north.unlock();
				if (south.isHeldByCurrentThread())
					south.unlock();
			}

		}
		if (myDirect == north) {
			try {
				south.lockInterruptibly();
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				north.lockInterruptibly();
				System.out.println("car to north has passed");
			} catch (InterruptedException e1) {
				System.out.println("car to north is killed");
			} finally {
				if (north.isHeldByCurrentThread())
					north.unlock();
				if (south.isHeldByCurrentThread())
					south.unlock();
			}

		}
	}

	public static void main(String[] args) throws InterruptedException {
		DeadLock car2south = new DeadLock(south);
		DeadLock car2north = new DeadLock(north);
		car2south.start();
		car2north.start();
		Thread.sleep(1000);
	}
}

  使用jps命令查看進程號爲32627,而後使用jstack -l 32637 > a.txt命令把堆棧信息打印到文件中,該文件內容以下:

2014-11-11 21:33:12
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.55-b03 mixed mode):

"Attach Listener" daemon prio=5 tid=0x00007f8d0c803000 nid=0x3307 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"DestroyJavaVM" prio=5 tid=0x00007f8d0b80b000 nid=0x1903 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"north" prio=5 tid=0x00007f8d0c075000 nid=0x5103 waiting on condition [0x0000000115b06000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000007d55ab600> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
	at DeadLock.run(DeadLock.java:48)

   Locked ownable synchronizers:
	- <0x00000007d55ab5d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

"south" prio=5 tid=0x00007f8d0c074800 nid=0x4f03 waiting on condition [0x0000000115a03000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000007d55ab5d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
	at DeadLock.run(DeadLock.java:28)

   Locked ownable synchronizers:
	- <0x00000007d55ab600> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

"Service Thread" daemon prio=5 tid=0x00007f8d0c025800 nid=0x4b03 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"C2 CompilerThread1" daemon prio=5 tid=0x00007f8d0c025000 nid=0x4903 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"C2 CompilerThread0" daemon prio=5 tid=0x00007f8d0d01b000 nid=0x4703 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"Signal Dispatcher" daemon prio=5 tid=0x00007f8d0c022000 nid=0x4503 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None

"Finalizer" daemon prio=5 tid=0x00007f8d0d004000 nid=0x3103 in Object.wait() [0x000000011526a000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000007d5505568> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
	- locked <0x00000007d5505568> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)

   Locked ownable synchronizers:
	- None

"Reference Handler" daemon prio=5 tid=0x00007f8d0d001800 nid=0x2f03 in Object.wait() [0x0000000115167000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000007d55050f0> (a java.lang.ref.Reference$Lock)
	at java.lang.Object.wait(Object.java:503)
	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
	- locked <0x00000007d55050f0> (a java.lang.ref.Reference$Lock)

   Locked ownable synchronizers:
	- None

"VM Thread" prio=5 tid=0x00007f8d0b83b000 nid=0x2d03 runnable 

"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007f8d0b818000 nid=0x2503 runnable 

"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007f8d0b819000 nid=0x2703 runnable 

"GC task thread#2 (ParallelGC)" prio=5 tid=0x00007f8d0d000000 nid=0x2903 runnable 

"GC task thread#3 (ParallelGC)" prio=5 tid=0x00007f8d0d001000 nid=0x2b03 runnable 

"VM Periodic Task Thread" prio=5 tid=0x00007f8d0c02e800 nid=0x4d03 waiting on condition 

JNI global references: 109


Found one Java-level deadlock:
=============================
"north":
  waiting for ownable synchronizer 0x00000007d55ab600, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "south"
"south":
  waiting for ownable synchronizer 0x00000007d55ab5d0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "north"

Java stack information for the threads listed above:
===================================================
"north":
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000007d55ab600> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
	at DeadLock.run(DeadLock.java:48)
"south":
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000007d55ab5d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
	at DeadLock.run(DeadLock.java:28)

Found 1 deadlock.

  從這個輸出能夠知道:

      一、在輸出的最後一段,有明確的"Found one Java-level deadlock"輸出,因此經過jstack命令咱們能夠檢測死鎖;

      二、輸出中包含了全部線程,除了咱們的north,sorth線程外,還有"Attach Listener", "C2 CompilerThread0", "C2 CompilerThread1"等等;

      三、每一個線程下面都會輸出當前狀態,以及這個線程當前持有鎖以及等待鎖,當持有與等待形成循環等待時,將致使死鎖。

1.七、jstatd命令

       jstatd命令是一個RMI服務器程序,它的做用至關於代理服務器,創建本地計算機與遠程監控工具的通訊,jstatd服務器可以將本機的Java應用程序信息傳遞到遠程計算機,因爲須要多臺計算機作演示,此處略。

1.八、hprof工具  

       hprof工具能夠用於監控Java應用程序在運行時的CPU信息和堆信息,關於hprof的官方文檔以下:https://docs.oracle.com/javase/7/docs/technotes/samples/hprof.html

 

二、Visual VM工具                                                                       

      Visual VM是一個功能強大的多合一故障診斷和性能監控的可視化工具,它集成了多種性能統計工具的功能,使用Visual VM能夠替代jstat、jmap、jhat、jstack等工具。在命令行輸入jvisualvm便可啓動visualvm。

      打開Visual VM以後,左邊導航欄會顯示出當前機器全部Java進程:

      點擊你想監控的程序便可對該程序進行監控,Visual VM的性能監控頁一共有如下幾個tab頁:

      概述頁會顯示程序的基本使用狀況,好比,進程ID,系統屬性,啓動參數等。

      經過監視頁面,能夠監視應用程序的CPU、堆、永久區、類加載器和線程數的總體狀況,經過頁面上的Perform GC和Heap Dump按鈕還能夠手動執行Full GC和生成堆快照。

      線程頁面會提供詳細的線程信息,單擊Thread Dump按鈕能夠導出當前全部線程的堆棧信息,若是Visual VM在當前線程中找到死鎖,則會以十分顯眼的方式在Threads頁面給予提示。

      抽樣器能夠對CPU和內存兩個性能進行抽樣,用於實時地監控程序。CPU採樣器能夠將CPU佔用時間定位到方法,內存採樣器能夠查看當前程序的堆信息。下面是一個頻繁調用的Java程序,咱們會對改程序進行採樣:

public class MethodTime {
	static java.util.Random r=new java.util.Random();
	static Map<String,String> map=null;
	static{
		map=new HashMap<String,String>();
		map.put("1", "Java");
		map.put("2", "C++");
		map.put("3", "Delphi");
		map.put("4", "C");
		map.put("5", "Phython");
	}
	public String getNameById(String id){
		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return map.get(id);
	}
	
	public List<String> getNamesByIds(String ids){
		List<String> re=new ArrayList<String>();
		String[] strs=ids.split(",");
		for(String id:strs){
			re.add(getNameById(id));
		}
		return re;
	}
	
	public List<String> getNamesByIdsBad(String ids){
		List<String> re=new ArrayList<String>();
		String[] strs=ids.split(",");
		for(String id:strs){
			//A bad code
			getNameById(id);
			re.add(getNameById(id));
		}
		return re;
	}
	
	public class NamesByIdsThread implements Runnable{
		@Override
		public void run() {
			try{
				while(true){
					int c=r.nextInt(4);
					String ids="";
					for(int i=0;i<c;i++)
						ids=Integer.toString((r.nextInt(4)+1))+",";
					getNamesByIds(ids);
				}
			}catch(Exception e){
			}
		}
	}
	
	public class NamesByIdsBadThread implements Runnable{
		@Override
		public void run() {
			try{
				while(true){
					int c=r.nextInt(4);
					String ids="";
					for(int i=0;i<c;i++)
						ids=Integer.toString((r.nextInt(4)+1))+",";
					getNamesByIdsBad(ids);
				}
			}catch(Exception e){
			}
		}
	}
	
	public static void main(String args[]){
		MethodTime instance=new MethodTime();
		new Thread(instance.new NamesByIdsThread()).start();
		new Thread(instance.new NamesByIdsBadThread()).start();
	}
}

  經過Visual VM的採樣功能,能夠找到改程序中佔用CPU時間最長的方法:

      默認Visual VM不統計內置對象的函數調用,好比java.*包中的類,若是要統計這些內置對象,單機右上角的設置進行調配。Visual VM雖然能夠統計方法的調用時間,可是沒法給出方法調用堆棧,Jprofile不只能夠給出方法調用時間,還能夠給出方法調用堆棧,較Visual VM更強大。

      右擊左導航的應用程序,會出現如下菜單:

      單機應用程序快照,能夠分析當前應用程序的快照,單擊堆Dump可以對當前的堆信息進行分析。Visual VM的更多使用方法,能夠查看Oracle的官方文檔https://docs.oracle.com/javase/7/docs/technotes/guides/visualvm/index.html

BTrace插件

      BTrace是一款功能強大的性能檢測工具,它能夠在不停機的狀況下,經過字節碼注入,動態監控系統的運行狀況,它能夠跟蹤指定的方法調用、構造函數調用和系統內存等信息,本部分打算舉一個例子,講解一下BTrace的使用。要在Visual VM中使用Btrace,首先須要安裝Btrace插件,點擊工具->插件便可在線安裝,安裝後右鍵應用程序,就會出現以下選項:

      點擊Trace application,便可進入BTrace插件界面。使用BTrace能夠監控指定函數的耗時,如下腳本經過正則表達式,監控全部類的getNameById方法:

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
	@TLS
    private static long startTime = 0;
    
    @OnMethod(clazz="/.+/", method="/getNameById/")//監控任意類的getNameById方法
    public static void startMethod() {
        startTime=timeMillis();
    }
    
    @OnMethod(clazz="/.+/", method="/getNameById/", 
    location=@Location(Kind.RETURN))//方法返回時觸發
    public static void endMethod() {
        print(strcat(strcat(name(probeClass()), "."), probeMethod()));
        print(" [");
        print(strcat("Time taken : ", str(timeMillis() - startTime)));
        println("]");
    }
}

  點擊運行,部分輸出以下:

MethodTime.getNameById [Time taken : 5]
MethodTime.getNameById [Time taken : 4]
MethodTime.getNameById [Time taken : 7]
MethodTime.getNameById [Time taken : 7]

  BTrace除了能夠監控函數耗時外,還能夠指定程序運行到某一行代碼觸發某一行爲,定時觸發行爲,監控函數參數等等。

三、MAT內存分析工具                                                                    

     MAT是一款功能強大的Java堆內存分析器,能夠用於查找內存泄露以及查看內存消耗狀況,MAT的官方文檔以下:http://help.eclipse.org/luna/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html

    在MAT中有淺堆和深堆的概念,淺堆是指一個對象結構所佔用的內存大小,深堆是指一個對象被GC回收後能夠真正釋放的內存大小。

    經過MAT,能夠列出全部垃圾回收的根對象,Java系統的根對象多是如下類:系統類,線程,Java局部變量,本地棧等等。在MAT中還能夠很清楚的看到根對象到當前對象的引用關係鏈。

    MAT還能夠自動檢測內存泄露,單擊菜單上的Leak Suspects命令,MAT會自動生成一份報告,這份報告羅列了系統內可能存在內存泄露的問題點。

    在MAT中,還能夠自動查找並顯示消耗內存最多的幾個對象,這些消耗大量內存的大對象每每是解決系統性能問題的關鍵所在。

    具體例子,略,網速太慢,至今還未下好。。

 

參考書籍:Java程序性能優化

相關文章
相關標籤/搜索