• [JDK工具-10] jvisualvm 多合一故障处理工具


    在这里插入图片描述

    1. 介绍

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

    • 用于监控、故障诊断以及性能分析Java应用程序。JVisualVM 通过集成多个命令行JDK工具(如 jconsole, jinfo, jmap, jstack 等)的功能于一体,为用户提供了一个统一且易用的图形界面

    位置:jdk\bin

    作用:

    • 查看应用jvm配置信息
    • 查看cpu、内存、类、线程监控信息
    • 查看堆的变化
    • 查看堆快照
    • 导出堆快照文件
    • 查看class对象加载信息
    • CPU分析:发现cpu使用率最高的方法
    • 分析死锁问题,找到死锁的代码

    界面如下:

    在这里插入图片描述

    2. 查看堆的变化

    每隔3秒,堆内存使用新增100M

    package com.xin.demo.threaddemo.bookdemo;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    public class JVisualVMDemo1 {
        public static final int _1M = 1024 * 1024;
        public static void main(String[] args) throws InterruptedException {
            List<Object> list = new ArrayList<>();
            for (int i = 0; i < 1000; i++) {
                list.add(new byte[100 * _1M]);
                TimeUnit.SECONDS.sleep(3);
                System.out.println(i);
            }
        }
    }
    

    查看内存变化

    在这里插入图片描述

    3. 查看堆快照

    点击“监视”->”堆(dump)”可以生产堆快照信息.

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    4. 导出堆快照文件

    点击右键另存为
    在这里插入图片描述
    可以用jvisualvm->文件->装入打开hprof文件,打开后如下图:
    在这里插入图片描述

    5. 查看class对象加载信息

    package com.xin.demo.threaddemo.bookdemo;
    
    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;
    import java.util.concurrent.TimeUnit;
    
    public class JVisualVMDemo2 {
        private static List<Object> insList = new ArrayList<>();
        public static void main(String[] args) throws Exception {
            permLeak();
        }
        private static void permLeak() throws Exception {
            for (int i = 0; i < 2000; 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);
                if (i % 100 == 0) {
                    TimeUnit.SECONDS.sleep(1);
                }
            }
        }
        private static URL[] getURLS() throws MalformedURLException {
            File libDir = new File("D:\\javasoft\\apache-maven-3.2.1\\mavenrepository\\commons-logging\\commons-logging\\1.2");
            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;
        }
    }
    
    

    下图,可以观察到元空间、类的变化成正相关

    在这里插入图片描述

    6. CPU分析:发现cpu使用率最高的方法

    CPU 性能分析的主要目的是统计函数的调用情况及执行时间,或者更简单的情况就是统计应用程序的 CPU 使用情况。
    没有程序运行时的 CPU 使用情况如下图:
    在这里插入图片描述

    下面我们写一个cpu占用率比较高的程序。

    package com.xin.demo.threaddemo.bookdemo;
    
    public class JVisualVMDemo3 {
        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);
            }
        }
    }
    
    

    在这里插入图片描述
    过高的 CPU 使用率可能是我们的程序代码性能有问题导致的。可以切换到“抽样器”对cpu进行采样,可以擦看到那个方法占用的cpu最高,然后进行优化。

    在这里插入图片描述
    从图中可以看出cpuFix方法使用cpu最多,然后就可以进行响应的优化了。

    7. 查看线程快照:发现死锁问题

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

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

    可以查看线程快照,发现系统的死锁问题。

    下面我们将通过visualvm来排查一个死锁问题。

    package com.xin.demo.threaddemo.bookdemo;
    
    public class JVisualVMDemo4 {
        public static void main(String[] args) {
            Obj1 obj1 = new Obj1();
            Obj2 obj2 = new Obj2();
            Thread thread1 = new Thread(new SynAddRunalbe(obj1, obj2, 1, 2, true));
            thread1.setName("thread1");
            thread1.start();
            Thread thread2 = new Thread(new SynAddRunalbe(obj1, obj2, 2, 1, false));
            thread2.setName("thread2");
            thread2.start();
        }
        /**
         * 线程死锁等待演示
         */
        public static class SynAddRunalbe implements Runnable {
            Obj1 obj1;
            Obj2 obj2;
            int a, b;
            boolean flag;
            public SynAddRunalbe(Obj1 obj1, Obj2 obj2, int a, int b, boolean flag) {
                this.obj1 = obj1;
                this.obj2 = obj2;
                this.a = a;
                this.b = b;
                this.flag = flag;
            }
            @Override
            public void run() {
                try {
                    if (flag) {
                        synchronized (obj1) {
                            Thread.sleep(100);
                            synchronized (obj2) {
                                System.out.println(a + b);
                            }
                        }
                    } else {
                        synchronized (obj2) {
                            Thread.sleep(100);
                            synchronized (obj1) {
                                System.out.println(a + b);
                            }
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public static class Obj1 {
        }
        public static class Obj2 {
        }
    }
    

    程序中:thread1持有obj1的锁,thread2持有obj2的锁,thread1等待获取obj2的锁,thread2等待获取obj1的锁,相互需要获取的锁都被对方持有者,造成了死锁。程序中出现了死锁的情况,我们是比较难以发现的。需要依靠工具解决。

    打开visualvm查看堆栈信息:

    在这里插入图片描述

    点击线程Dump,生成线程堆栈信息:

    在这里插入图片描述

    上面这段信息可以看出,thread1持有Obj1对象的锁,等待获取Obj2的锁,thread2持有Obj2的锁,等待获取Obj1的锁,导致了死锁。

  • 相关阅读:
    【css】css奇数、偶数、指定数选择器:
    [Hive] CTE 通用表达式 WITH关键字
    如何使用 ABAP 代码发送邮件到指定邮箱试读版
    【网路安全 --- Linux,window常用命令】网络安全领域,Linux和Windows常用命令,记住这些就够了,收藏起来学习吧!!
    油封的正常使用寿命是多长?
    使用busybox快速制作initramfs
    粒子滤波PF—从贝叶斯滤波到粒子滤波PF——Part-I(贝叶斯滤波)
    企业为什么要上OA?
    接口技术:输入输出接口部分笔记
    30岁从事软件测试,目前已失业4个月,迷茫不知该怎么办。?
  • 原文地址:https://blog.csdn.net/weixin_43024834/article/details/139248805