• jvm 分析调优工具-学习笔记


    jvm 分析调优工具

    jps命令查看java进程pid

    jps 列出java进程名(不完整类名) 和pid

    在这里插入图片描述

    jps -l 列出java进程名(完整类名) 和pid

    在这里插入图片描述

    jmap命令查看java进程占用的jvm资源情况

    jmap -histo pid 查看内存情况

    在这里插入图片描述

    jmap -heap pid 查看java程序的堆占用信息

    在这里插入图片描述

    jmap -dump 导出heap快照分析文件heap.hprof

    在这里插入图片描述

    生成的heap.hprof文件可以使用mat工具或jvisualvm工具 进行分析。

    可以在jar启动时配置 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./(指定路径)
    这样就能设置内存溢出自动导出dump文件到指定的路径
    
    • 1
    • 2

    mat工具是第三方厂商提供的工具,

    一般使用jdk自带的jvisualvm 工具查看导出的heap.hprof文件
    在这里插入图片描述

    控制台输入jvisualvm命令即可打开jdk自带的可视化jvm分析工具
    在这里插入图片描述

    点文件->装入 ,把heap.hprof文件导入到visualVM里,就可以看到堆dump的详细信息
    在这里插入图片描述

    jinfo命令查看java程序的jvm参数和sys配置参数

    jinfo查看java程序的jvm参数

    jinfo -flags pid 可以看到程序的Xmx Xms等参数配置情况
    
    • 1

    在这里插入图片描述

    jinfo查看java程序的系统参数

    查看java系统参数,使用命令 jinfo -sysprops pid
    
    • 1

    在这里插入图片描述

    jstat命令查看堆内存各部分的使用量已经加载类的数量

    gc垃圾回收统计 jstat -gc pid

    在这里插入图片描述

    堆内存统计 jstat -gccapacity pid

    在这里插入图片描述

    新生代垃圾回收统计 jstat -gcnew pid

    在这里插入图片描述

    新生代内存统计 jstat -gcnewcapacity pid

    在这里插入图片描述

    老年代垃圾回收统计 jstat -gcold pid

    在这里插入图片描述

    老年代内存统计 jstat -gcoldcapacity pid

    在这里插入图片描述

    元数据空间统计 jstat -gcmetacapacity pid

    在这里插入图片描述

    垃圾回收简化 jstat -gcutil pid

    在这里插入图片描述

    场景1-分析线上应用OOM后生成的dump文件

    线上应用的jvm启动参数配置

    -Xms28m -Xmx56m -XX:+HeapDumpOnOutOfMemoryError
    
    • 1

    代码案例OOMTest.java

    package cn.demo;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class OOMTest {
    
    
        public static void main(String[] args) {
            
            List<ThirdUser>  list = new ArrayList<>();
    
            while (true){  //while true 死循环,会使程序一直执行
                ThirdUser thirdUser = new ThirdUser();
                thirdUser.setUsername("1");
                thirdUser.setAge(1);
                thirdUser.setHobby("ctrl");
                list.add(thirdUser); //由于配置了-Xmx56m,当程序占用的jvm内存大于56m后会报OOM
            }
        }
    }
    
    class ThirdUser {
    
        private String username;
    
        private Integer age;
    
        private String hobby;
    
        public ThirdUser() {
        }
    
        public ThirdUser(String username, Integer age, String hobby) {
            this.username = username;
            this.age = age;
            this.hobby = hobby;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getHobby() {
            return hobby;
        }
    
        public void setHobby(String hobby) {
            this.hobby = hobby;
        }
    
        @Override
        public String toString() {
            return "ThirdUser{" +
                    "username='" + username + '\'' +
                    ", age=" + age +
                    ", hobby='" + hobby + '\'' +
                    '}';
        }
    }
    
    • 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

    启动运行后,会报错OutOfMemoryError

    java.lang.OutOfMemoryError: Java heap space
    Dumping heap to java_pid5768.hprof ...
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    	at java.util.Arrays.copyOf(Arrays.java:3210)
    	at java.util.Arrays.copyOf(Arrays.java:3181)
    	at java.util.ArrayList.grow(ArrayList.java:265)
    	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
    	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
    	at java.util.ArrayList.add(ArrayList.java:462)
    	at cn.demo.OOMTest.main(OOMTest.java:18)
    Heap dump file created [106019329 bytes in 0.408 secs]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这个java程序根目录下会生成 dump文件 java_pid5768.hprof ,然后,使用jvisualvm 或 jprofiler 工具进行分析hprof文件。

    这里以jvisualvm工具为例,把java_pid5768.hprof导入到jvisualvm后,可以看到如下界面
    在这里插入图片描述

    在这里插入图片描述

    场景2-利用jstack命令排查程序死锁问题

    代码案例MustDeadLockDemo.java

    package cn.demo;
    
    public class MustDeadLockDemo {
    
        static class DeadLockTask implements Runnable{
            private boolean flag;
            private Object lock1;
            private Object lock2;
            public DeadLockTask(boolean flag, Object lock1, Object lock2) {
                this.flag = flag;
                this.lock1 = lock1;
                this.lock2 = lock2;
            }
            @Override
            public void run() {
                if (flag){
                    synchronized (lock1){
                        System.out.println(Thread.currentThread().getName()+"->拿到锁1");
                        try {
                            Thread.sleep(1000);
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"->等待锁2释放...");
                        synchronized (lock2){
                            System.out.println(Thread.currentThread().getName()+"->拿到锁2");
                        }
                    }
                }
                if (!flag){
                    synchronized (lock2){
                        System.out.println(Thread.currentThread().getName()+"->拿到锁2");
                        try {
                            Thread.sleep(1000);
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"->等待锁1释放...");
                        synchronized (lock1){
                            System.out.println(Thread.currentThread().getName()+"->拿到锁1");
                        }
                    }
                }
            }
        }
    
        public static void main(String[] args){
            Object lock1 = new Object();
            Object lock2 = new Object();
            new Thread(new DeadLockTask(true,lock1,lock2),"线程1").start();
            new Thread(new DeadLockTask(false,lock1,lock2),"线程2").start();
        }
    }
    
    • 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

    这个程序执行会因为拿不到对方的锁而一直阻塞,无法结束。

    首先使用 jps命令找到这个java进程的pid

    jps -l
    
    • 1

    然后使用jstack 命令查看这个java进程的线程dump信息

    jstack pid
    
    • 1

    在这里插入图片描述

    在这里插入图片描述

    解决死锁的办法:打破多线程对资源的死锁占用,使其存在先后执行、先后占用的顺序。

    场景3-使用jstack找出占用cpu最高的线程堆栈信息

    代码案例CpuMaxTest.java

    package cn.demo;
    
    public class CpuMaxTest {
        public static final int initData = 66;
        public static UserTest user = new UserTest();
        private UserTest user2 = new UserTest();
        public int compute(){
            int a =1;
            int b =2;
            int c = (a+b)*10;
            return c;
        }
        public static void main(String[] args) {
            CpuMaxTest test = new CpuMaxTest();
            while (true){
                test.compute();
            }
        }
    }
    
    class UserTest {
        private String uid;
        private String name;
        UserTest() {
        }
        UserTest(String uid, String name) {
            this.uid = uid;
            this.name = name;
        }
    }
    
    • 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

    这个程序执行后会导致CPU占用 99%,排查步骤如下

    1用 jps -l 列出java进程的进程号 和 进程名

    jps -l
    
    • 1

    在这里插入图片描述

    2使用命令 top -p pid ,查看这个java进程在linux的内存情况,pid是你的java进程号

    top -p  8318
    
    • 1

    在这里插入图片描述

    3按大写的H键 ,获取每个线程的内存情况

    在这里插入图片描述

    4找到内存和CPU占用最高的线程tid ,比如1804

    5将1804转换成16进制,使用命令

    printf '%x\n' 1804
    
    • 1

    在这里插入图片描述

    6执行 如下的jstack 命令,得到线程堆栈信息中 0x2119这个线程所在行后面10行的信息。

     jstack  进程pid | grep -A 10  线程id的16进制
    
    • 1

    在这里插入图片描述

    7根据上面的堆栈信息找出可能存在问题的代码。

  • 相关阅读:
    没有二十年功力,写不出这一行“看似无用”的代码!
    贪心算法-Huffman算法
    算法竞赛进阶指南 基本算法 0x06 倍增
    iNFTnews | NFT在门票领域会有哪些作用?
    计算机毕业设计JAVA的IT技术交流和分享平台的设计与实现mybatis+源码+调试部署+系统+数据库+lw
    SpringCloud (四) ——Nacos配置管理
    酷开科技夯实流量基础,构建智慧生活新风尚!
    kubeadm 部署方式修改kube-proxy为 ipvs模式
    自动化攻击背景下的过去、现在与未来
    [面试直通版]数据库核心之索引,性能与安全(下)
  • 原文地址:https://blog.csdn.net/ThinkPet/article/details/130897296