Java性能調優工具

Java性能調優工具

1、JDK命令行工具                                                                       

1.1、jps命令

      jps用於列出Java的進程,jps可以增加參數,-m用於輸出傳遞給Java進程的參數,-l用於輸出主函數的完整路徑,-v可以用於顯示傳遞給jvm的參數。

1

2

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應用程序運行時信息的工具,它的功能非常強大,可以通過它查看堆信息的詳細情況,它的基本使用方法爲:

1

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

  選項option可以由以下值組成:

1

2

3

4

5

6

7

8

9

10

11

12

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,下面是該參數的輸出介紹以及一個簡單例子:  

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

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可以查看運行時參數:

1

2

jinfo -flag MaxTenuringThreshold 31518

-XX:MaxTenuringThreshold=15

  jinfo還可以在運行時修改參數值:

1

2

3

4

5

> jinfo -flag PrintGCDetails 31518

-XX:-PrintGCDetails

> jinfo -flag +PrintGCDetails 31518

> jinfo -flag PrintGCDetails 31518

-XX:+PrintGCDetails

1.4、jmap命令

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

1

2

3

> 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應用程序的堆快照文件,使用命令如下:

1

2

3

4

5

6

7

8

9

10

> 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代碼如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

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命令把堆棧信息打印到文件中,該文件內容如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

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程序,我們會對改程序進行採樣:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

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方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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("]");

    }

}

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

1

2

3

4

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中,還可以自動查找並顯示消耗內存最多的幾個對象,這些消耗大量內存的大對象往往是解決系統性能問題的關鍵所在。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章