Java性能调优工具

释放双眼,带上耳机,听听看~!

**1、JDK命令行工具                                                                       **

1.1、jps命令

      jps用于列出Java的进程,jps可以增加参数,-m用于输出传递给Java进程的参数,-l用于输出主函数的完整路径,-v可以用于显示传递给jvm的参数。


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

1.2、jstat命令

      jstat是一个可以用于观察Java应用程序运行时信息的工具,它的功能非常强大,可以通过它查看堆信息的详细情况,它的基本使用方法为:


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

选项option可以由以下值组成:


1
2
3
4
5
6
7
8
9
10
11
12
13
1jstat -class pid:显示加载class的数量,及所占空间等信息。  
2    jstat -compiler pid:显示VM实时编译的数量等信息。  
3    jstat -gc pid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。  
4    jstat -gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。  
5    jstat -gcnew pid:new对象的信息。  
6    jstat -gcnewcapacity pid:new对象的信息及其占用量。  
7    jstat -gcold pid:old对象的信息。  
8    jstat -gcoldcapacity pid:old对象的信息及其占用量。  
9    jstat -gcpermcapacity pid: perm对象的信息及其占用量。  
10    jstat -gcutil pid:统计gc信息统计。  
11    jstat -printcompilation pid:当前VM执行的信息。  
12    除了以上一个参数外,还可以同时加上 两个数字,如:jstat -printcompilation 3024 250 6是每250毫秒打印一次,一共打印6次。
13

      这些参数中最常用的参数是gcutil,下面是该参数的输出介绍以及一个简单例子:  


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1S0  — Heap上的 Survivor space 0 区已使用空间的百分比  
2S1  — Heap上的 Survivor space 1 区已使用空间的百分比  
3E   — Heap上的 Eden space 区已使用空间的百分比  
4O   — Heap上的 Old space 区已使用空间的百分比  
5P   — Perm space 区已使用空间的百分比  
6YGC — 从应用程序启动到采样时发生 Young GC 的次数  
7YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)  
8FGC — 从应用程序启动到采样时发生 Full GC 的次数  
9FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)  
10GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)  
11  
12实例使用1:  
13  
14[root@localhost bin]# jstat -gcutil 25444  
15  
16  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT  
17  
18 11.63   0.00   56.46  66.92  98.49 162    0.248    6      0.331    0.579
19

1.3、jinfo命令

      jinfo可以用来查看正在运行的Java应用程序的扩展参数,甚至在运行时修改部分参数,它的基本语法为:


1
2
1jinfo  <option>  <pid>
2

jinfo可以查看运行时参数:


1
2
3
1jinfo -flag MaxTenuringThreshold 31518
2-XX:MaxTenuringThreshold=15
3

jinfo还可以在运行时修改参数值:


1
2
3
4
5
6
1> jinfo -flag PrintGCDetails 31518
2-XX:-PrintGCDetails
3> jinfo -flag +PrintGCDetails 31518
4> jinfo -flag PrintGCDetails 31518
5-XX:+PrintGCDetails
6

1.4、jmap命令

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


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

获得堆快照文件之后,我们可以使用多种工具对文件进行分析,例如jhat,visual vm等。

1.5、jhat命令

      使用jhat工具可以分析Java应用程序的堆快照文件,使用命令如下:


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

jhat在分析完成之后,使用HTTP服务器展示其分析结果,在浏览器中访问http://127.0.0.1:7000/即可得到分析结果。

1.6、jstack命令

jstack可用于导出Java应用程序的线程堆栈信息,语法为:


1
2
1jstack -l <pid>
2

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
68
1public class DeadLock extends Thread {
2   protected Object myDirect;
3   static ReentrantLock south = new ReentrantLock();
4   static ReentrantLock north = new ReentrantLock();
5
6   public DeadLock(Object obj) {
7       this.myDirect = obj;
8       if (myDirect == south) {
9           this.setName("south");
10      }
11      if (myDirect == north) {
12          this.setName("north");
13      }
14  }
15
16  @Override
17  public void run() {
18      if (myDirect == south) {
19          try {
20              north.lockInterruptibly();
21              try {
22                  Thread.sleep(500);
23              } catch (Exception e) {
24                  e.printStackTrace();
25              }
26              south.lockInterruptibly();
27              System.out.println("car to south has passed");
28          } catch (InterruptedException e1) {
29              System.out.println("car to south is killed");
30          } finally {
31              if (north.isHeldByCurrentThread())
32                  north.unlock();
33              if (south.isHeldByCurrentThread())
34                  south.unlock();
35          }
36
37      }
38      if (myDirect == north) {
39          try {
40              south.lockInterruptibly();
41              try {
42                  Thread.sleep(500);
43              } catch (Exception e) {
44                  e.printStackTrace();
45              }
46              north.lockInterruptibly();
47              System.out.println("car to north has passed");
48          } catch (InterruptedException e1) {
49              System.out.println("car to north is killed");
50          } finally {
51              if (north.isHeldByCurrentThread())
52                  north.unlock();
53              if (south.isHeldByCurrentThread())
54                  south.unlock();
55          }
56
57      }
58  }
59
60  public static void main(String[] args) throws InterruptedException {
61      DeadLock car2south = new DeadLock(south);
62      DeadLock car2north = new DeadLock(north);
63      car2south.start();
64      car2north.start();
65      Thread.sleep(1000);
66  }
67}
68

使用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
137
12014-11-11 21:33:12
2Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.55-b03 mixed mode):
3
4"Attach Listener" daemon prio=5 tid=0x00007f8d0c803000 nid=0x3307 waiting on condition [0x0000000000000000]
5   java.lang.Thread.State: RUNNABLE
6
7   Locked ownable synchronizers:
8   - None
9
10"DestroyJavaVM" prio=5 tid=0x00007f8d0b80b000 nid=0x1903 waiting on condition [0x0000000000000000]
11   java.lang.Thread.State: RUNNABLE
12
13   Locked ownable synchronizers:
14  - None
15
16"north" prio=5 tid=0x00007f8d0c075000 nid=0x5103 waiting on condition [0x0000000115b06000]
17   java.lang.Thread.State: WAITING (parking)
18  at sun.misc.Unsafe.park(Native Method)
19  - parking to wait for  <0x00000007d55ab600> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
20  at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
21  at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
22  at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
23  at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
24  at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
25  at DeadLock.run(DeadLock.java:48)
26
27   Locked ownable synchronizers:
28  - <0x00000007d55ab5d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
29
30"south" prio=5 tid=0x00007f8d0c074800 nid=0x4f03 waiting on condition [0x0000000115a03000]
31   java.lang.Thread.State: WAITING (parking)
32  at sun.misc.Unsafe.park(Native Method)
33  - parking to wait for  <0x00000007d55ab5d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
34  at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
35  at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
36  at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
37  at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
38  at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
39  at DeadLock.run(DeadLock.java:28)
40
41   Locked ownable synchronizers:
42  - <0x00000007d55ab600> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
43
44"Service Thread" daemon prio=5 tid=0x00007f8d0c025800 nid=0x4b03 runnable [0x0000000000000000]
45   java.lang.Thread.State: RUNNABLE
46
47   Locked ownable synchronizers:
48  - None
49
50"C2 CompilerThread1" daemon prio=5 tid=0x00007f8d0c025000 nid=0x4903 waiting on condition [0x0000000000000000]
51   java.lang.Thread.State: RUNNABLE
52
53   Locked ownable synchronizers:
54  - None
55
56"C2 CompilerThread0" daemon prio=5 tid=0x00007f8d0d01b000 nid=0x4703 waiting on condition [0x0000000000000000]
57   java.lang.Thread.State: RUNNABLE
58
59   Locked ownable synchronizers:
60  - None
61
62"Signal Dispatcher" daemon prio=5 tid=0x00007f8d0c022000 nid=0x4503 runnable [0x0000000000000000]
63   java.lang.Thread.State: RUNNABLE
64
65   Locked ownable synchronizers:
66  - None
67
68"Finalizer" daemon prio=5 tid=0x00007f8d0d004000 nid=0x3103 in Object.wait() [0x000000011526a000]
69   java.lang.Thread.State: WAITING (on object monitor)
70  at java.lang.Object.wait(Native Method)
71  - waiting on <0x00000007d5505568> (a java.lang.ref.ReferenceQueue$Lock)
72  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
73  - locked <0x00000007d5505568> (a java.lang.ref.ReferenceQueue$Lock)
74  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
75  at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)
76
77   Locked ownable synchronizers:
78  - None
79
80"Reference Handler" daemon prio=5 tid=0x00007f8d0d001800 nid=0x2f03 in Object.wait() [0x0000000115167000]
81   java.lang.Thread.State: WAITING (on object monitor)
82  at java.lang.Object.wait(Native Method)
83  - waiting on <0x00000007d55050f0> (a java.lang.ref.Reference$Lock)
84  at java.lang.Object.wait(Object.java:503)
85  at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
86  - locked <0x00000007d55050f0> (a java.lang.ref.Reference$Lock)
87
88   Locked ownable synchronizers:
89  - None
90
91"VM Thread" prio=5 tid=0x00007f8d0b83b000 nid=0x2d03 runnable
92
93"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007f8d0b818000 nid=0x2503 runnable
94
95"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007f8d0b819000 nid=0x2703 runnable
96
97"GC task thread#2 (ParallelGC)" prio=5 tid=0x00007f8d0d000000 nid=0x2903 runnable
98
99"GC task thread#3 (ParallelGC)" prio=5 tid=0x00007f8d0d001000 nid=0x2b03 runnable
100
101"VM Periodic Task Thread" prio=5 tid=0x00007f8d0c02e800 nid=0x4d03 waiting on condition
102
103JNI global references: 109
104
105
106Found one Java-level deadlock:
107=============================
108"north":
109  waiting for ownable synchronizer 0x00000007d55ab600, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
110  which is held by "south"
111"south":
112  waiting for ownable synchronizer 0x00000007d55ab5d0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
113  which is held by "north"
114
115Java stack information for the threads listed above:
116===================================================
117"north":
118 at sun.misc.Unsafe.park(Native Method)
119 - parking to wait for  <0x00000007d55ab600> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
120 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
121 at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
122 at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
123 at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
124 at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
125 at DeadLock.run(DeadLock.java:48)
126"south":
127 at sun.misc.Unsafe.park(Native Method)
128 - parking to wait for  <0x00000007d55ab5d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
129 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
130 at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
131 at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
132 at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
133 at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
134 at DeadLock.run(DeadLock.java:28)
135
136Found 1 deadlock.
137

从这个输出可以知道:

      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进程:

Java性能调优工具

      点击你想监控的程序即可对该程序进行监控,Visual VM的性能监控页一共有以下几个tab页:

Java性能调优工具

      概述页会显示程序的基本使用情况,比如,进程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
79
1public class MethodTime {
2   static java.util.Random r=new java.util.Random();
3   static Map<String,String> map=null;
4   static{
5       map=new HashMap<String,String>();
6       map.put("1", "Java");
7       map.put("2", "C++");
8       map.put("3", "Delphi");
9       map.put("4", "C");
10      map.put("5", "Phython");
11  }
12  public String getNameById(String id){
13      try {
14          Thread.sleep(1);
15      } catch (InterruptedException e) {
16          e.printStackTrace();
17      }
18      return map.get(id);
19  }
20 
21  public List<String> getNamesByIds(String ids){
22      List<String> re=new ArrayList<String>();
23      String[] strs=ids.split(",");
24      for(String id:strs){
25          re.add(getNameById(id));
26      }
27      return re;
28  }
29 
30  public List<String> getNamesByIdsBad(String ids){
31      List<String> re=new ArrayList<String>();
32      String[] strs=ids.split(",");
33      for(String id:strs){
34          //A bad code
35          getNameById(id);
36          re.add(getNameById(id));
37      }
38      return re;
39  }
40 
41  public class NamesByIdsThread implements Runnable{
42      @Override
43      public void run() {
44          try{
45              while(true){
46                  int c=r.nextInt(4);
47                  String ids="";
48                  for(int i=0;i<c;i++)
49                      ids=Integer.toString((r.nextInt(4)+1))+",";
50                  getNamesByIds(ids);
51              }
52          }catch(Exception e){
53          }
54      }
55  }
56 
57  public class NamesByIdsBadThread implements Runnable{
58      @Override
59      public void run() {
60          try{
61              while(true){
62                  int c=r.nextInt(4);
63                  String ids="";
64                  for(int i=0;i<c;i++)
65                      ids=Integer.toString((r.nextInt(4)+1))+",";
66                  getNamesByIdsBad(ids);
67              }
68          }catch(Exception e){
69          }
70      }
71  }
72 
73  public static void main(String args[]){
74      MethodTime instance=new MethodTime();
75      new Thread(instance.new NamesByIdsThread()).start();
76      new Thread(instance.new NamesByIdsBadThread()).start();
77  }
78}
79

通过Visual VM的采样功能,可以找到改程序中占用CPU时间最长的方法:

Java性能调优工具

      默认Visual VM不统计内置对象的函数调用,比如java.*包中的类,如果要统计这些内置对象,单机右上角的设置进行调配。Visual VM虽然可以统计方法的调用时间,但是无法给出方法调用堆栈,Jprofile不仅可以给出方法调用时间,还可以给出方法调用堆栈,较Visual VM更强大。

      右击左导航的应用程序,会出现以下菜单:

Java性能调优工具

      单机应用程序快照,可以分析当前应用程序的快照,单击堆Dump能够对当前的堆信息进行分析。Visual VM的更多使用方法,可以查看Oracle的官方文档https://docs.oracle.com/javase/7/docs/technotes/guides/visualvm/index.html

BTrace插件

      BTrace是一款功能强大的性能检测工具,它可以在不停机的情况下,通过字节码注入,动态监控系统的运行情况,它可以跟踪指定的方法调用、构造函数调用和系统内存等信息,本部分打算举一个例子,讲解一下BTrace的使用。要在Visual VM中使用Btrace,首先需要安装Btrace插件,点击工具->插件即可在线安装,安装后右键应用程序,就会出现如下选项:

Java性能调优工具

      点击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
23
1import com.sun.btrace.annotations.*;
2import static com.sun.btrace.BTraceUtils.*;
3
4@BTrace
5public class TracingScript {
6   @TLS
7    private static long startTime = 0;
8    
9    @OnMethod(clazz="/.+/", method="/getNameById/")//监控任意类的getNameById方法
10    public static void startMethod() {
11        startTime=timeMillis();
12    }
13    
14    @OnMethod(clazz="/.+/", method="/getNameById/",
15    location=@Location(Kind.RETURN))//方法返回时触发
16    public static void endMethod() {
17        print(strcat(strcat(name(probeClass()), "."), probeMethod()));
18        print(" [");
19        print(strcat("Time taken : ", str(timeMillis() - startTime)));
20        println("]");
21    }
22}
23

点击运行,部分输出如下:


1
2
3
4
5
1MethodTime.getNameById [Time taken : 5]
2MethodTime.getNameById [Time taken : 4]
3MethodTime.getNameById [Time taken : 7]
4MethodTime.getNameById [Time taken : 7]
5

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中,还可以自动查找并显示消耗内存最多的几个对象,这些消耗大量内存的大对象往往是解决系统性能问题的关键所在。

    具体例子,略,网速太慢,至今还未下好。。

 

参考书籍:Java程序性能优化

给TA打赏
共{{data.count}}人
人已打赏
安全经验

Google AdSense 全面解析(申请+操作+作弊+忠告)

2021-10-11 16:36:11

安全经验

安全咨询服务

2022-1-12 14:11:49

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索