1. 程式人生 > >Java效能調優工具

Java效能調優工具

 1.1、jps命令       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.2、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,下面是該引數的輸出介紹以及一個簡單例子:   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.3、jinfo命令       jinfo可以用來檢視正在執行的Java應用程式的擴充套件引數,甚至在執行時修改部分引數,它的基本語法為: 1 jinfo  <option>  <pid>   jinfo可以檢視執行時引數: 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.4、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.5、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.6、jstack命令   jstack可用於匯出Java應用程式的執行緒堆疊資訊,語法為: 1 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.   從這個輸出可以知道:       1、在輸出的最後一段,有明確的"Found one Java-level deadlock"輸出,所以通過jstack命令我們可以檢測死鎖;       2、輸出中包含了所有執行緒,除了我們的north,sorth執行緒外,還有"Attach Listener", "C2 CompilerThread0", "C2 CompilerThread1"等等;       3、每個執行緒下面都會輸出當前狀態,以及這個執行緒當前持有鎖以及等待鎖,當持有與等待造成迴圈等待時,將導致死鎖。 1.7、jstatd命令        jstatd命令是一個RMI伺服器程式,它的作用相當於代理伺服器,建立本地計算機與遠端監控工具的通訊,jstatd伺服器能夠將本機的Java應用程式資訊傳遞到遠端計算機,由於需要多臺計算機做演示,此處略。 1.8、hprof工具          hprof工具可以用於監控Java應用程式在執行時的CPU資訊和堆資訊,關於hprof的官方文件如下:https://docs.oracle.com/javase/7/docs/technotes/samples/hprof.html 2、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除了可以監控函式耗時外,還可以指定程式執行到某一行程式碼觸發某一行為,定時觸發行為,監控函式引數等等。 3、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中,還可以自動查詢並顯示消耗記憶體最多的幾個物件,這些消耗大量記憶體的大物件往往是解決系統性能問題的關鍵所在。