JVM调优工具详解

JVM调优工具详解学习了JVM的一些调优工具为大家分享一下,现在把学习笔记总结记录一下,如果记录有些错误,还望指出。文章目录前言jdk自带工具一、Jmap1.1jps1.2jmap-histo1.3jmap-heap1.4jmap-dump二、Jstack三、Jinfo四、Jstat垃圾回收统计JVM运行情况预估年轻代对象增长的速率YoungGC的触发频率和每次耗时每次YoungGC后有多少对象存活和进入老年代FullGC的触发频率和每次耗时内存泄露到底是怎么回事五、ArthasArthas使用git

大家好,又见面了,我是你们的朋友全栈君。

学习了JVM的一些调优工具为大家分享一下,现在把学习笔记总结记录一下,如果记录有些错误,还望指出。


前言

对于大家代码中可能出现死锁或者oom查看GC日志而无从下手时,今天博主也是为大家介绍一下原生的jdk的命令自带的监控工具jvisualvm以及目前开源比较火热的Arthas进行一个介绍话不多说开始今天的分享


jdk自带工具

一、Jmap

1.1 jps

事先启动一个web应用程序,用jps查看其进程id,接着用各种jdk自带命令优化应用
在这里插入图片描述

1.2 jmap -histo

可以使用“jmp -histo 进程id” 此命令可以用来查看内存信息,实例个数以及占用内存大小打开log.txt,文件内容如下:

在这里插入图片描述

  • num:序号
  • instances:实例数量
  • bytes:占用空间大小
  • class name:类名称,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]

1.3 jmap -heap

查看对应进程id堆信息可以看到下面打印出堆的配置最大小容量等及Eden区From和Old区使用情况
在这里插入图片描述

1.4 jmap -dump

可以直接使用命令导出选择进程ID的dump文件

jmap ‐dump:format=b,file=eureka.hprof 14660
在这里插入图片描述

  1. -XX:+HeapDumpOnOutOfMemoryError
  2. -XX:HeapDumpPath=./ (路径)
    也可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来)

代码如下:

package com.jvm;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class OOMTest { 
   

    public static List<Object> list = new ArrayList<>();

    // JVM设置
    // ‐Xms10M ‐Xmx10M ‐XX:+PrintGCDetails ‐XX:+HeapDumpOnOutOfMemoryError ‐XX:HeapDumpPath=D:\jvm.dump
    public static void main(String[] args) { 
   
        List<Object> list = new ArrayList<>();
        int i = 0;
        int j = 0;

        while (true) { 
   
            list.add(new User(i++, UUID.randomUUID().toString()));
            new User(j--, UUID.randomUUID().toString());
        }
    }
}

当然也可以用jvisualvm命令工具导入该dump文件分析使用jvisualvm就更简单了只需要在cmd下输入jvisualvm就可以打开
在这里插入图片描述
导入输出的dump文件
在这里插入图片描述

二、Jstack

用jstack加进程id查找死锁,代码见如下示例:

package com.jvm;

public class DeadLockTest { 
   

    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static void main(String[] args) { 
   
        new Thread(() ->{ 
   
            synchronized (lock1) { 
   
                try { 
   
                    System.out.println("thread1 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) { 
   

                }
                synchronized (lock2) { 
   
                    System.out.println("thread1 end");
                }
            }
        }).start();

        new Thread(() ->{ 
   
            synchronized (lock2) { 
   
                try { 
   
                    System.out.println("thread2 begin");
                    Thread.sleep(5000);
                } catch (InterruptedException e) { 
   

                }
                synchronized (lock1) { 
   
                    System.out.println("thread2 end");
                }
            }
        }).start();

        System.out.println("main thread end");
    }


}

运行后打印线程详细信息
在这里插入图片描述
Thread-1″ 线程名
prio=5 优先级=5 tid=0x000000001fa9e000 线程id
nid=0x2d64 线程对应的本地线程标识nid
java.lang.Thread.State: BLOCKED 线程状态

在这里插入图片描述
图上详细的说明了Thread-0与Thread-1相互引用而造成死锁就连代码出现的行号也是打印出来了。

还可以用jvisualvm自动检测死锁只需要查看对应运行java的进程即可
在这里插入图片描述

三、Jinfo

查看正在运行的Java应用程序的扩展参数 查看jvm的参数

查看jvm的参数 jinfo -flags 进程ID
在这里插入图片描述
查看java系统参数 jinfo -sysprops进程ID
在这里插入图片描述

四、Jstat

jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下:
jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]
注意:使用的jdk版本是jdk8

垃圾回收统计

jstat -gc pid 最常用,可以评估程序内存使用及GC压力整体情况
在这里插入图片描述

  • S0C:第一个幸存区的大小,单位KB
  • S1C:第二个幸存区的大小
  • S0U:第一个幸存区的使用大小
  • S1U:第二个幸存区的使用大小
  • EC:伊甸园区的大小
  • EU:伊甸园区的使用大小
  • OC:老年代大小
  • OU:老年代使用大小
  • MC:方法区大小(元空间)
  • MU:方法区使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间使用大小
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间,单位s
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间,单位s
  • GCT:垃圾回收消耗总时间,单位s

JVM运行情况预估

用 jstat gc -pid 命令可以计算出如下一些关键数据,有了这些数据就可以采用之前介绍过的优化思路,先给自己的系统设置一些初始性的 JVM参数,比如堆内存大小,年轻代大小,Eden和Survivor的比例,老年代的大小,大对象的阈值,大龄对象进入老年代的阈值等。

年轻代对象增长的速率

可以执行命令 jstat -gc pid 1000 10 (每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对 象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不 同的时间分别估算不同情况下对象增长速率。

Young GC的触发频率和每次耗时

知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC 公式算出,根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。

每次Young GC后有多少对象存活和进入老年代

这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden, survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次 Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。

Full GC的触发频率和每次耗时

知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。

优化思路其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年 代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。

内存泄露到底是怎么回事

再给大家讲一种情况,一般电商架构可能会使用多级缓存架构,就是redis加上JVM级缓存,大多数同学可能为了图方便对于JVM级缓存就 简单使用一个hashmap,于是不断往里面放缓存数据,但是很少考虑这个map的容量问题,结果这个缓存map越来越大,一直占用着老 年代的很多空间,时间长了就会导致full gc非常频繁,这就是一种内存泄漏,对于一些老旧数据没有及时清理导致一直占用着宝贵的内存 资源,时间长了除了导致full gc,还有可能导致OOM。
这种情况完全可以考虑采用一些成熟的JVM级缓存框架来解决,比如ehcache等自带一些LRU数据淘汰算法的框架来作为JVM级的缓存。

五、Arthas

Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具。支持 JDK6+, 采用命令行交互模式,可以方便的定位和诊断 线上程序运行问题。Arthas 官方文档十分详细,详见:https://arthas.aliyun.com/en-us/

Arthas使用

github下载arthas

wget https://alibaba.github.io/arthas/arthas‐boot.jar

或者 Gitee 下载

wget https://arthas.gitee.io/arthas‐boot.jar

用java -jar运行即可,可以识别机器上所有Java进程(我们这里之前已经运行了一个Arthas测试程序,代码见下方)

package com.jvm;

import java.util.HashSet;

public class Arthas { 
   

    private static HashSet hashSet = new HashSet();

    public static void main(String[] args) { 
   
        // 模拟 CPU 过高
        cpuHigh();
        // 模拟线程死锁
        deadThread();
        // 不断的向 hashSet 集合增加数据
        addHashSetThread();
    }

    /** * 不断的向 hashSet 集合添加数据 */
    public static void addHashSetThread() { 
   
        // 初始化常量
        new Thread(()-> { 
   
            int count = 0;
            while (true){ 
   
                try { 
   
                    hashSet.add("count" + count);
                    Thread.sleep(1000);
                    count++;
                }catch (InterruptedException e) { 
   
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void cpuHigh() { 
   
        new Thread(()->{ 
   
            while (true){ 
   

            }
        }).start();
    }

    /** * 死锁 */
    private static void deadThread() { 
   
        /** 创建资源 */
        Object resourceA = new Object();
        Object resourceB = new Object();

        Thread threadA = new Thread(()->{ 
   
           synchronized (resourceA){ 
   
               System.out.println(Thread.currentThread() + " get ResourceA");
               try { 
   
                   Thread.sleep(1000);
               }catch (InterruptedException e) { 
   
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread() + "waiting get resourceB");
               synchronized (resourceB){ 
   
                   System.out.println(Thread.currentThread() + " get resourceB");
               }
           }
        });
        Thread threadB = new Thread(()->{ 
   
            synchronized (resourceB){ 
   
                System.out.println(Thread.currentThread() + " get ResourceB");
                try { 
   
                    Thread.sleep(1000);
                }catch (InterruptedException e) { 
   
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resourceA");
                synchronized (resourceA){ 
   
                    System.out.println(Thread.currentThread() + " get resourceA");
                }
            }
        });
        threadA.start();
        threadB.start();
    }
}

在这里插入图片描述
选择进程序号1,进入进程信息操作
在这里插入图片描述

dashboard

输入dashboard可以查看整个进程的运行情况,线程、内存、GC、运行环境信息:
在这里插入图片描述

thread

输入thread可以查看线程详细情况
在这里插入图片描述
输入 thread加上线程ID 可以查看线程堆栈
在这里插入图片描述
输入 thread -b 可以查看线程死锁
在这里插入图片描述

其实Arthas这个工具并没有大家想象的那么难使用起来非常的简单博主也只是简单的为大家展示一下功能具体的命令可以用help命令查看,或查看文档:https://arthas.aliyun.com/doc/advanced-use.html#id2 更多的还是需要自己去摸索


总结

以上就是今天要分享的内容,如有表述不清、错误还望海涵并指出。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/138245.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月31日 下午7:36
下一篇 2022年5月31日 下午7:36


相关推荐

  • 如何反编译dll文件

    如何反编译dll文件DLL DynamicLinkL 文件为动态链接库文件 又称 应用程序拓展 是软件文件类型 下载并解压 NETReflector 下面下载为绿色 7 3 0 18 版本 注 此为英文版 百度有汉化版下载 链接 https pan baidu com s 1eoWFe62wA uOdn3wTQ7e w 密码 gt87

    2025年11月13日
    4
  • cnpm安装与vue安装[通俗易懂]

    cnpm安装与vue安装[通俗易懂]首先是cnpm安装,在安装之前首先确定安装好了nodejs并且配置好了环境终端cmd后输入npm查看版本确定结果正确后进行cnpm安装终端输入npminstall-gcnpm–registry=https://registry.npm.taobao.org这里安装的是淘宝的镜像,等待安装完成后输入cnpm-v查看版本如果出现不是命令检查你的path路径确定安装成功后,终端键入:cnpminstallvue等待安装完成键入vue-V查看安装是否成功默认安装

    2022年10月15日
    5
  • imx8 usb otg模式切换

    imx8 usb otg模式切换内核驱动名称 drivers usb chipidea debug cdrivers usb chipidea core cdrivers usb chipidea ci hdrc imx cDTS 文件节点 fsl imx8dx dtsiusbotg1 usb 5b0d0000 compatible fsl imx8qm usb fsl imx2

    2026年3月26日
    2
  • 深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件「建议收藏」

    深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件「建议收藏」在求解最优化问题中,拉格朗日乘子法(LagrangeMultiplier)和KKT(KarushKuhnTucker)条件是两种最常用的方法。在有等式约束时使用拉格朗日乘子法,在有不等约束时使用KKT条件。  我们这里提到的最优化问题通常是指对于给定的某一函数,求其在指定作用域上的全局最小值(因为最小值与最大值可以很容易转化,即最大值问题可以转化成最小值问题)。提到KKT条件一般会附带的…

    2022年10月8日
    6
  • file_get_contents 返回null,allow_url_fopen = On如何解决

    file_get_contents 返回null,allow_url_fopen = On如何解决绝对有效开启fsockopen函数iis6打开allow_url_fopen设置发表时间:2013-12-0611:48来源:未知分类:其它代码作者:岑溪网站开发点击:75次烦爆了,今天装个自己租了个有了务器,想装discuz两个函数allow_url_fopen和file_get_contents都不能成功开启。为什么不能成功开启,按照网上说的allow_

    2022年7月21日
    15
  • 蓝天bios怎么开启安全启动

    蓝天bios怎么开启安全启动

    2026年3月15日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号