java 自带的监控工具 VisualVM
java 自带的监控工具 VisualVM
注:在Linux中,该工具的打开方式是在终端输入jvisualvm
VisualVM 是一款免费的,集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优。这些功能包括生成和分析海量数据、跟踪内存泄漏、监控垃圾回收器、执行内存和 CPU 分析,同时它还支持在 MBeans 上进行浏览和操作。本文主要介绍如何使用 VisualVM 进行性能分析及调优。
准备工作
自从 JDK 6 Update 7 以后已经作为 Oracle JDK 的一部分,位于 JDK 根目录的 bin 文件夹下,无需安装,直接运行即可。
内存分析篇
VisualVM 通过检测 JVM 中加载的类和对象信息等帮助我们分析内存使用情况,我们可以通过 VisualVM 的监视标签对应用程序进行内存分析。
1)内存堆 Heap
首先我们来看内存堆 Heap 使用情况,我本机 eclipse 的进程在 visualVM 显示如下:

随便写个小程序占用内存大的,运行一下
程序如下:
[
](javascript:void(0)😉
1 package jvisualVM;
3 public class JavaHeapTest {
4 public final static int OUTOFMEMORY = 200000000;
6 private String oom;
8 private int length;
10 StringBuffer tempOOM = new StringBuffer();
12 public JavaHeapTest(int leng) {
13 this.length = leng;
15 int i = 0;
16 while (i < leng) {
17 i++;
18 try {
19 tempOOM.append("a");
20 } catch (OutOfMemoryError e) {
21 e.printStackTrace();
22 break;
23 }
24 }
25 this.oom = tempOOM.toString();
27 }
29 public String getOom() {
30 return oom;
31 }
33 public int getLength() {
34 return length;
35 }
37 public static void main(String[] args) {
38 JavaHeapTest javaHeapTest = new JavaHeapTest(OUTOFMEMORY);
39 System.out.println(javaHeapTest.getOom().length());
40 }
42 }
[
](javascript:void(0)😉
查看 VisualVM Monitor tab, 堆内存变大了

在程序运行结束之前, 点击 Heap Dump 按钮, 等待一会儿,得到 dump 结果,可以看到一些 Summary 信息
点击 Classes, 发现 char[] 所占用的内存是最大的

双击它,得到如下 Instances 结果

Instances 是按 Size 由大到小排列的
第一个就是最大的, 展开 Field 区域的 values

StringBuffer 类型的 全局变量 tempOOM 占用内存特别大, 注意局部变量是无法通过 堆 dump 来得到分析结果的。
另外,对于 “堆 dump” 来说,在远程监控 jvm 的时候,VisualVM 是没有这个功能的,只有本地监控的时候才有。
### 转载注明出处:http://www.cnblogs.com/wade-xu/p/4369094.html
2) 永久保留区域 PermGen
其次来看下永久保留区域 PermGen 使用情况
运行一段类加载的程序,代码如下:
[
](javascript:void(0)😉
1 package jvisualVM;
3 import java.io.File;
4 import java.lang.reflect.Method;
5 import java.net.MalformedURLException;
6 import java.net.URL;
7 import java.net.URLClassLoader;
8 import java.util.ArrayList;
9 import java.util.List;
11 public class TestPermGen {
13 private static List<Object> insList = new ArrayList<Object>();
15 public static void main(String[] args) throws Exception {
17 permLeak();
18 }
20 private static void permLeak() throws Exception {
21 for (int i = 0; i < 1000; i++) {
22 URL[] urls = getURLS();
23 URLClassLoader urlClassloader = new URLClassLoader(urls, null);
24 Class<?> logfClass = Class.forName("org.apache.commons.logging.LogFactory", true,urlClassloader);
25 Method getLog = logfClass.getMethod("getLog", String.class);
26 Object result = getLog.invoke(logfClass, "TestPermGen");
27 insList.add(result);
28 System.out.println(i + ": " + result);
29 }
30 }
32 private static URL[] getURLS() throws MalformedURLException {
33 File libDir = new File("C:/Users/wadexu/.m2/repository/commons-logging/commons-logging/1.1.1");
34 File[] subFiles = libDir.listFiles();
35 int count = subFiles.length;
36 URL[] urls = new URL[count];
37 for (int i = 0; i < count; i++) {
38 urls[i] = subFiles[i].toURI().toURL();
39 }
40 return urls;
41 }
44 }
[
](javascript:void(0)😉
一个类型装载之后会创建一个对应的 java.lang.Class 实例,这个实例本身和普通对象实例一样存储于堆中,我觉得之所以说是这是一种特殊的实例,某种程度上是因为其充当了访问 PermGen 区域中类型信息的代理者。
运行一段时间后抛 OutOfMemoryError 了, VisualVM 监控结果如下:

结论:PermGen 区域分配的堆空间过小,我们可以通过设置 - XX: PermSize 参数和 - XX:MaxPermSize 参数来解决。
关于 PermGen OOM 深入分析请参考这篇文章
关于 Perform GC, 请参考这篇文章
### 转载注明出处:http://www.cnblogs.com/wade-xu/p/4369094.html
CPU 分析篇
CPU 性能分析的主要目的是统计函数的调用情况及执行时间,或者更简单的情况就是统计应用程序的 CPU 使用情况。
没有程序运行时的 CPU 使用情况如下图:

运行一段 占用 CPU 的小程序,代码如下
[
](javascript:void(0)😉
package jvisualVM;
public class MemoryCpuTest {
public static void main(String[] args) throws InterruptedException {
cpuFix();
}
/**
* cpu 运行固定百分比
*
* @throws InterruptedException
*/
public static void cpuFix() throws InterruptedException {
// 80%的占有率
int busyTime = 8;
// 20%的占有率
int idelTime = 2;
// 开始时间
long startTime = 0;
while (true) {
// 开始时间
startTime = System.currentTimeMillis();
/*
* 运行时间
*/
while (System.currentTimeMillis() - startTime < busyTime) {
;
}
// 休息时间
Thread.sleep(idelTime);
}
}
}
[
](javascript:void(0)😉
查看监视页面 Monitor tab

过高的 CPU 使用率可能是由于我们的项目中存在低效的代码;
在我们对程序施压的时候,过低的 CPU 使用率也有可能是程序的问题。
点击取样器 Sampler, 点击 “CPU” 按钮, 启动 CPU 性能分析会话,VisualVM 会检测应用程序所有的被调用的方法,
在 CPU samples tab 下可以看到我们的方法 cpufix() 的自用时间最长, 如下图:

切换到 Thread CPU Time 页面下,我们的 main 函数这个进程 占用 CPU 时间最长, 如下图:

### 转载注明出处:http://www.cnblogs.com/wade-xu/p/4369094.html
线程分析篇
Java 语言能够很好的实现多线程应用程序。当我们对一个多线程应用程序进行调试或者开发后期做性能调优的时候,往往需要了解当前程序中所有线程的运行状态,是否有死锁、热锁等情况的发生,从而分析系统可能存在的问题。
在 VisualVM 的监视标签内,我们可以查看当前应用程序中所有活动线程(Live threads)和守护线程(Daemon threads)的数量等实时信息。
运行一段小程序,代码如下:
[
](javascript:void(0)😉
package jvisualVM;
public class MyThread extends Thread{
public static void main(String[] args) {
MyThread mt1 = new MyThread("Thread a");
MyThread mt2 = new MyThread("Thread b");
mt1.setName("My-Thread-1 ");
mt2.setName("My-Thread-2 ");
mt1.start();
mt2.start();
}
public MyThread(String name) {
}
public void run() {
while (true) {
}
}
}
[
](javascript:void(0)😉
Live threads 从 11 增加两个 变成 13 了
Daemon threads 从 8 增加两个 变成 10 了

VisualVM 的线程标签提供了三种视图,默认会以时间线的方式展现, 如下图:
可以看到两个我们 run 的程序里启的线程:My-Thread-1 和 My-Thread-2

另外还有两种视图分别是表视图和详细信息视图, 这里看一下每个 Thread 的详细视图:

### 转载注明出处:http://www.cnblogs.com/wade-xu/p/4369094.html
再来一段死锁的程序,看 VisualVM 能否分析出来
[
](javascript:void(0)😉
package jvisualVM;
public class DeadLock {
public static void main(String[] args) {
Resource r1 = new Resource();
Resource r0 = new Resource();
Thread myTh1 = new LockThread1(r1, r0);
Thread myTh0 = new LockThread0(r1, r0);
myTh1.setName("DeadLock-1 ");
myTh0.setName("DeadLock-0 ");
myTh1.start();
myTh0.start();
}
}
class Resource {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
class LockThread1 extends Thread {
private Resource r1, r2;
public LockThread1(Resource r1, Resource r2) {
this.r1 = r1;
this.r2 = r2;
}
@Override
public void run() {
int j = 0;
while (true) {
synchronized (r1) {
System.out.println("The first thread got r1's lock " + j);
synchronized (r2) {
System.out.println("The first thread got r2's lock " + j);
}
}
j++;
}
}
}
class LockThread0 extends Thread {
private Resource r1, r2;
public LockThread0(Resource r1, Resource r2) {
this.r1 = r1;
this.r2 = r2;
}
@Override
public void run() {
int j = 0;
while (true) {
synchronized (r2) {
System.out.println("The second thread got r2's lock " + j);
synchronized (r1) {
System.out.println("The second thread got r1's lock" + j);
}
}
j++;
}
}
}
[
](javascript:void(0)😉
打开 VisualVM 检测到的 JVM 进程,我们可以看到这个 tab 在闪,VisualVM 已经检测到我这个 package 下面的 DeadLock 类出错了
切换到 Thread tab, 可以看到死锁了, Deadlock detected!
另外可以点击 Thread Dump 线程转储,进一步分析,在这里就不赘述了,有兴趣的读者可以自行实验。


浙公网安备 33010602011771号