VisualVM使用方法

转自:http://www.cnblogs.com/wade-xu/p/4369094.html

这篇总结的很不错(本人亲自操手学习),留着以后复习备用,很适合入门级的学习者:

VisualVM 是一款免费的,集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优。这些功能包括生成和分析海量数据、跟踪内存泄漏、监控垃圾回收器、执行内存和 CPU 分析,同时它还支持在 MBeans 上进行浏览和操作。本文主要介绍如何使用 VisualVM 进行性能分析及调优。

准备工作

自从 JDK 6 Update 7 以后已经作为 Oracle JDK 的一部分,位于 JDK 根目录的 bin 文件夹下,无需安装,直接运行即可。

 内存分析篇

VisualVM 通过检测 JVM 中加载的类和对象信息等帮助我们分析内存使用情况,我们可以通过 VisualVM 的监视标签对应用程序进行内存分析。

1)内存堆Heap

首先我们来看内存堆Heap使用情况,我本机eclipse的进程在visualVM显示如下:

VisualVM使用方法_第1张图片

随便写个小程序占用内存大的,运行一下

程序如下:

复制代码

 1 package jvisualVM;
 2 
 3 public class JavaHeapTest {
 4     public final static int OUTOFMEMORY = 200000000;
 5     
 6     private String oom;
 7 
 8     private int length;
 9     
10     StringBuffer tempOOM = new StringBuffer();
11 
12     public JavaHeapTest(int leng) {
13         this.length = leng;
14        
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();
26 
27     }
28 
29     public String getOom() {
30         return oom;
31     }
32 
33     public int getLength() {
34         return length;
35     }
36 
37     public static void main(String[] args) {
38         JavaHeapTest javaHeapTest = new JavaHeapTest(OUTOFMEMORY);
39         System.out.println(javaHeapTest.getOom().length());
40     }
41 
42 }

复制代码

查看VisualVM Monitor tab, 堆内存变大了

VisualVM使用方法_第2张图片

在程序运行结束之前, 点击Heap Dump 按钮, 等待一会儿,得到dump结果,可以看到一些Summary信息

点击Classes, 发现char[]所占用的内存是最大的
VisualVM使用方法_第3张图片

双击它,得到如下Instances结果

VisualVM使用方法_第4张图片

Instances是按Size由大到小排列的

第一个就是最大的, 展开Field区域的 values

VisualVM使用方法_第5张图片

StringBuffer类型的 全局变量 tempOOM 占用内存特别大, 注意局部变量是无法通过 堆dump来得到分析结果的。

另外,对于“堆 dump”来说,在远程监控jvm的时候,VisualVM是没有这个功能的,只有本地监控的时候才有。

  ###转载注明出处:http://www.cnblogs.com/wade-xu/p/4369094.html

 

2)永久保留区域PermGen

其次来看下永久保留区域PermGen使用情况

运行一段类加载的程序,代码如下:

复制代码

 1 package jvisualVM;
 2 
 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;
10 
11 public class TestPermGen {
12     
13     private static List insList = new ArrayList();
14 
15     public static void main(String[] args) throws Exception {
16 
17         permLeak();
18     }
19 
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     }
31 
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     }
42 
43     
44 } 
  

复制代码

一个类型装载之后会创建一个对应的java.lang.Class实例,这个实例本身和普通对象实例一样存储于堆中,我觉得之所以说是这是一种特殊的实例,某种程度上是因为其充当了访问PermGen区域中类型信息的代理者。

 运行一段时间后抛OutOfMemoryError了, VisualVM监控结果如下:


VisualVM使用方法_第6张图片

结论:PermGen区域分配的堆空间过小,我们可以通过设置-XX: PermSize参数和-XX:MaxPermSize参数来解决。

关于PermGen OOM深入分析请参考这篇文章

关于Perform GC, 请参考这篇文章

###转载注明出处:http://www.cnblogs.com/wade-xu/p/4369094.html

 

CPU分析篇

CPU 性能分析的主要目的是统计函数的调用情况及执行时间,或者更简单的情况就是统计应用程序的 CPU 使用情况。

没有程序运行时的 CPU 使用情况如下图:

VisualVM使用方法_第7张图片

 

运行一段 占用CPU 的小程序,代码如下

复制代码

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);
        }
    }
}

复制代码

查看监视页面 Monitor tab

VisualVM使用方法_第8张图片

 

过高的 CPU 使用率可能是由于我们的项目中存在低效的代码;

在我们对程序施压的时候,过低的 CPU 使用率也有可能是程序的问题。

 

点击取样器Sampler, 点击“CPU”按钮, 启动CPU性能分析会话,VisualVM 会检测应用程序所有的被调用的方法,

在CPU samples tab 下可以看到我们的方法cpufix() 的自用时间最长, 如下图:

VisualVM使用方法_第9张图片

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

VisualVM使用方法_第10张图片

 ###转载注明出处:http://www.cnblogs.com/wade-xu/p/4369094.html

线程分析篇

Java 语言能够很好的实现多线程应用程序。当我们对一个多线程应用程序进行调试或者开发后期做性能调优的时候,往往需要了解当前程序中所有线程的运行状态,是否有死锁、热锁等情况的发生,从而分析系统可能存在的问题。

在 VisualVM 的监视标签内,我们可以查看当前应用程序中所有活动线程(Live threads)和守护线程(Daemon threads)的数量等实时信息。

 

运行一段小程序,代码如下:

复制代码

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) {
            
        }
    }
    

}

复制代码

Live threads 从11增加两个 变成13了

Daemon threads从8增加两个 变成10了 

VisualVM使用方法_第11张图片

 

VisualVM 的线程标签提供了三种视图,默认会以时间线的方式展现, 如下图:

可以看到两个我们run的程序里启的线程:My-Thread-1 和 My-Thread-2

VisualVM使用方法_第12张图片

 

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

VisualVM使用方法_第13张图片

 ###转载注明出处:http://www.cnblogs.com/wade-xu/p/4369094.html 

 再来一段死锁的程序,看VisualVM 能否分析出来

复制代码

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++;
            }
        }
    
    }

复制代码

打开VisualVM检测到的JVM进程,我们可以看到这个tab在闪,VisualVM已经检测到我这个package下面的DeadLock类出错了

切换到Thread tab, 可以看到死锁了, Deadlock detected!

另外可以点击Thread Dump 线程转储,进一步分析,在这里就不赘述了,有兴趣的读者可以自行实验。

 VisualVM使用方法_第14张图片

 

------------------------------------

 

1、VisualVM 简介


VisualVM 是一个工具,它提供了一个可视界面,用于查看 Java 虚拟机 (Java Virtual Machine, JVM) 上运行的基于 Java 技术的应用程序(Java 应用程序)的详细信息。VisualVM 对 Java Development Kit (JDK) 工具所检索的 JVM 软件相关数据进行组织,并通过一种使您可以快速查看有关多个 Java 应用程序的数据的方式提供该信息。您可以查看本地应用程序以及远程主机上运行的应用程序的相关数据。此外,还可以捕获有关 JVM 软件实例的数据,并将该数据保存到本地系统,以供后期查看或与其他用户共享。 
为了能充分利用所有的 Java VisualVM 功能,应运行 Java Platform, Standard Edition (Java SE) 版本 6。

2、Tomcat远程监控配置


2.1启动visualvm 
命令行运行jvisualvm

2.2JMX 配置 
VisualVM使用方法_第15张图片

远程机器的程序需要加上JVM参数 
-Dcom.sun.management.jmxremote=true 
-Dcom.sun.management.jmxremote.port=8099(配置远程 connection 的端口号的) 
-Dcom.sun.management.jmxremote.ssl=false(指定了 JMX 是否启用 ssl) 
-Dcom.sun.management.jmxremote.authenticate=false( 指定了JMX 是否启用鉴权(需要用户名,密码鉴权)) 
-Djava.rmi.server.hostname=192.168.0.1(配置 server 的 IP)

备注:另外需要检查 hostname –i,看解析出来是否为本地的IP,如是127.0.0.1或者IP为多个IP中之一,则其他的IP无效,会连接不上。

2.3jstatd 配置

找到需要远程连接的Linux服务器的jdk的bin目录,在其下面建立一个指定安全策略的文件 jstatd.policy 文件,内容如下:

grant codebase “file:${java.home}/../lib/tools.jar” { 
permission java.security.AllPermission; 
}; 
然后在远程主机上启动 jstatd 并且不要关闭。 
启动命令:jstatd -J-Djava.security.policy=jstatd.policy -p 1099 
这样就可以在 JVisualVM 上,添加远程主机,并且设置 jstatd 的端口就可以了。

3、Dump


VisualVM 能够生成堆转储,统计某一特定时刻 JVM 中的对象信息,帮助我们分析对象的引用关系、是否有内存泄漏情况的发生等。

3.1堆dump 
VisualVM使用方法_第16张图片
当 VisualVM 统计完堆内对象数据后,会把堆转储信息显示在新的堆转储标签内,我们可以看到摘要、类、实例数等信息以及通过 OQL 控制台执行查询语句功能。 
A. 堆转储的摘要包括转储的文件大小、路径等基本信息,运行的系统环境信息,也可以显示所有的线程信息。

B. 从类视图可以获得各个类的实例数和占用堆大小数,分析出内存空间的使用情况,找出内存的瓶颈,避免内存的过度使用。

C. 还能对两个堆转储文件进行比较。通过比较我们能够分析出两个时间点哪些对象被大量创建或销毁。

3.2线程Dump

VisualVM使用方法_第17张图片
线程状态: 
 RUNNABLE: 运行中状态,可能里面还能看到locked字样,表明它获得了某把锁。 
 BLOCKED:被某个锁(synchronizers)給block住了。 
 WAITING:等待某个condition或monitor发生,一般停留在park(), wait(), sleep(),join() 等语句里。 
 TIME_WAITING:和WAITING的区别是wait() 等语句加上了时间限制 wait(timeout)。

4、CPU


VisualVM 能够监控应用程序在一段时间的 CPU 的使用情况,显示 CPU 的使用率、方法的执行效率和频率等相关数据帮助我们发现应用程序的性能瓶颈。我们可以通过 VisualVM 的监视标签和抽样器标签对应用程序进行 CPU 性能分析。 
4.1CPU 监视 
在监视标签内,我们可以查看 CPU 的使用率以及垃圾回收活动对性能的影响。 
1、过高的 CPU 使用率可能是由于我们的项目中存在低效的代码,可以通过 Profiler 标签的 CPU 性能分析功能进行详细的分析。 
2、如果垃圾回收活动过于频繁,占用了较高的 CPU 资源,可能是由内存不足或者是新生代和旧生代分配不合理导致的等。

在抽样器标签,点击“CPU”按钮启动一个 CPU 性能分析会话 ,VisualVM 会检测应用程序所有的被调用的方法。当进入一个方法时,线程会发出一个“method entry”的事件,当退出方法时同样会发出一个“method exit”的事件,这些事件都包含了时间戳。然后 VisualVM 会把每个被调用方法的总的执行时间和调用的次数按照运行时长展示出来。 
此外,我们也可以通过性能分析结果下方的方法名过滤器对分析结果进行过滤。

4.2 CPU 快照 
当有一个性能分析会话(内存或者 CPU)正在进行时,我们可以通过性能分析结果工具栏的“快照”按钮生成 Profiler 快照捕获当时的性能分析数据。

5、线程


Java 语言能够很好的实现多线程应用程序。当我们对一个多线程应用程序进行调试或者开发后期做性能调优的时候,往往需要了解当前程序中所有线程的运行状态,是否有死锁、热锁等情况的发生,从而分析系统可能存在的问题。

5.1线程监视 
在 VisualVM 的监视标签内,我们可以查看当前应用程序中所有活动线程和守护线程的数量等实时信息。

5.2线程插件安装 
1、从主菜单中选择“工具”>“插件”。

2、在“可用插件”标签中,选中该插件的“安装”复选框。单击“安装”。

5.3死锁例子 
从线程图中,我们可以很轻易地找到这种可疑特征:两个(或多个)线程长期同时处于”监视(被阻塞)”状态,提示“检查到死锁”说明它们出现了死锁,应当生成dump查到详细内容。 
2、生成dump后,在dump的最下面有关于锁的说明

你可能感兴趣的:(VisualVM使用方法)