使用VisualVM分析性能

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


目录:

  准备工作

  内存分析篇

    内存堆Heap

    永久保留区域PermGen

  CPU分析篇

  线程分析篇

  参考文献


准备工作

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

内存分析篇

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

1)内存堆Heap

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

图片发自简书App


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

程序如下:

package jvisualVM;

public class JavaHeapTest {

    public final static int OUTOFMEMORY = 200000000;

    private String oom;

    private int length;

    StringBuffer tempOOM = new StringBuffer();

    public JavaHeapTest(int leng) {

        this.length = leng;

        int i = 0;

        while (i < leng) {

            i++;

            try {

                tempOOM.append("a");

            } catch (OutOfMemoryError e) {

              e.printStackTrace();

              break;

            }

        }

        this.oom = tempOOM.toString();

    }

    public String getOom() {

        return oom;

    }

    public int getLength() {

        return length;

    }

    public static void main(String[] args) {

        JavaHeapTest javaHeapTest = new JavaHeapTest(OUTOFMEMORY);

        System.out.println(javaHeapTest.getOom().length());

    }

}

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

图片发自简书App


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

点击Classes, 发现char[]所占用的内存是最大的

图片发自简书App


双击它,得到如下Instances结果

图片发自简书App

Instances是按Size由大到小排列的

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

图片发自简书App

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

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


2)永久保留区域PermGen

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

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

package jvisualVM;

import java.io.File;

import java.lang.reflect.Method;

import java.net.MalformedURLException;

import java.net.URL;

import java.net.URLClassLoader;

import java.util.ArrayList;

import java.util.List;

public class TestPermGen {

    private static List

    public static void main(String[] args) throws Exception {

        permLeak();

    }

    private static void permLeak() throws Exception {

        for (int i = 0; i < 1000; i++) {

            URL[] urls = getURLS();

            URLClassLoader urlClassloader = new URLClassLoader(urls, null);

            Class logfClass = Class.forName("org.apache.commons.logging.LogFactory", true,urlClassloader);

            Method getLog = logfClass.getMethod("getLog", String.class);

            Object result = getLog.invoke(logfClass, "TestPermGen");

            insList.add(result);

            System.out.println(i + ": " + result);

        }

    }

    private static URL[] getURLS() throws MalformedURLException {

        File libDir = new File("C:/Users/wadexu/.m2/repository/commons-logging/commons-logging/1.1.1");

        File[] subFiles = libDir.listFiles();

        int count = subFiles.length;

        URL[] urls = new URL[count];

        for (int i = 0; i < count; i++) {

            urls[i] = subFiles[i].toURI().toURL();

        }

        return urls;

    }

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

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

图片发自简书App

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

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

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

这部分知识还是比较深入的,有空还要继续研究。


CPU分析篇

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

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

图片发自简书App

运行一段 占用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

图片发自简书App

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

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

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

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

图片发自简书App

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

图片发自简书App


线程分析篇

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了

图片发自简书App

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

图片发自简书App


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

图片发自简书App

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

图片发自简书App


再来一段死锁的程序,看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 线程转储,进一步分析,在这里就不赘述了,有兴趣的读者可以自行实验。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,537评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,160评论 11 349
  • 0×1.ACL概述ACL(Access Control List,访问控制列表),是一系列运用到路由器接口的指令列...
    Zero___阅读 2,647评论 0 3
  • PS基础知识小科普,么么哒! 提到PS的画笔预设,很多小伙伴都会很快想到Brush的诸多技能点,尤其是丰富的abr...
    阿随向前冲阅读 4,363评论 4 25