JVM调优工具详解

前置工作,启动一个WEB项目,用jps查看这个应用的进程号;

jmap

jps命令查询应用的进程号:
在这里插入图片描述
执行命令 jmap -histo 53152 > ./jmap.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[][]

堆信息
jmap -heap 47352
在这里插入图片描述
可以导出dump文件到当前目录下;
在这里插入图片描述
然后用使用jvisualvm软件进行导入显示
在这里插入图片描述
也是显示堆内存中的对象个数和大小;

可以设置内存溢出自动导出dump文件:添加启动参数
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\jvm.dump

public class MainClass1 {
    //-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(j--, UUID.randomUUID().toString()));
        }
    }
}

在这里插入图片描述

Jstack

用jstack加进程id查找死锁
在这里插入图片描述
死锁代码:

public class MainClass1 {
    static Object obj1 = new Object();
    static Object obj2 = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (obj1) {
                System.out.println("thread1 start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj2) {
                    System.out.println("thread1 and");
                }
            }
        }).start();
        new Thread(() -> {
            synchronized (obj2) {
                System.out.println("thread2 start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj1) {
                    System.out.println("thread2 and");
                }
            }

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

    }
}

在这里插入图片描述
Thead-1:线程名
prio=5:优先级=5
tid=0x000001f75d288800:线程id
nid=0x9634:线程对应的本地线程nid
java.lang.Thread.State: RUNNABLE :线程状态

下面是检查到的死锁:
在这里插入图片描述
jvisualvm也可以检测到死锁;
在这里插入图片描述
Jvisualvm远程连接服务器:
启动普通的jar程序JMX端口配置,
java ‐Dcom.sun.management.jmxremote.port=8888 ‐Djava.rmi.server.hostname=192.168.50.60 ‐Dcom.sun.management.jmxremote.ssl=false ‐Dcom.sun.management.jmxremote.authenticate=false ‐jar microservice‐eureka‐server.jar

tomcat的JMX配置:在catalina.sh文件里的最后一个JAVA_OPTS的赋值语句下一行增加如下配置行

JAVA_OPTS=“$JAVA_OPTS ‐Dcom.sun.management.jmxremote.port=8888 ‐Djava.rmi.server.hostname=192.168.50.60 ‐Dcom.sun.ma
nagement.jmxremote.ssl=false ‐Dcom.sun.management.jmxremote.authenticate=false”

jstack找出占用cpu最高线程的堆栈信息:

1、top命令先找出占用最高CUP的进程:
在这里插入图片描述
2、top -p
显示出java进程的内存情况
在这里插入图片描述
3、按大写H,获取每个线程的CUP的使用情况

得到占用CUP最高的线程pid 10662
把pid转成十六进制29a6,为线程的十六进制id

4、执行jstack 10662|grep -A 10 29a6,得到线程十六进制id为29a6的堆栈信息所在行的后10行;
就能找到对应占用CPU高的代码

在这里插入图片描述

Jinfo

查询正在运行java程序的启动扩展参数
在这里插入图片描述
jinfo -sysprops 10854查看java的系统参数
在这里插入图片描述

Jstat

查看内存的jvm的使用情况及自定义查看jvm内存的变化和GC情况;
Jstat -gc pid:查看jvm内存的使用和GC的情况
在这里插入图片描述
S0C:年轻代的servivor0区大小;单位KB
S1C:年轻代的servivor1区大小
SOU:servivor0的已使用大小
S1U:servivor1的已使用大小
EC:eden区的大小
EU:eden区已使用大小
OC:老年代大小
OU:老年代已使用大小
MC:方法区大小
MU:方法区已使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间已使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间,单位S
FGC:Full GC 次数
FGCT: Full GC 消耗时间,单位S
GC: 垃圾回收消耗总时间。

新生代垃圾回收统计:jstat -gcnew 19848
在这里插入图片描述
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:最大幸存1区大小
S0C:当前幸存1区大小
S1CMX:最大幸存2区大小
S1C:当前幸存2区大小
ECMX:最大伊甸园区大小
EC:当前伊甸园区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数

老年代垃圾回收统计:jstat -gcold 19848
在这里插入图片描述
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

元空间和垃圾回收统计:jstat -gcmetacapacity 19848
在这里插入图片描述
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

Jvm内存使用比例和垃圾回收统计:jstat -gcutil 19848
在这里插入图片描述
S0:幸存1区当前使用比例
S1:幸存2区当前使用比例
E:伊甸园区使用比例
O:老年代使用比例
M:元数据区使用比例
CCS:压缩使用比例
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

对JVM运行情况进行预估

jstat -gc 19848 1000 10 每隔一秒执行一次命令,共执行10次;

  • 计算年轻代的增长速率:通过观察EU来估算每秒Eden区新增长多少对象;如果系统负载不高,可以提高观察频率,把1秒提高到1分钟也是可以的;注意,一般系统可能有高峰期和日常期,所以需要在不同情况不同情况下估算对象的增长速率。
  • Yang GC的触发频率和每次耗时:知道了年轻代对象的增长速率和Edun区的大小就能大概计算出Yang GC的出发频率; 通过YGCT/YGC可以计算出每次耗时;
  • Yang GC后大概有多少对象存活和有多少对象进入老年代:在这之前要知道Yang GC的触发频率,假设是每5分钟执行一次,那么执行命令 jstat -gc 19848 300000
    10,观察每次Eden、suvivor和老年代使用的变化情况,在每次GC之后Eden都会大幅减少,suvivor和老年代都可能增长,这些增长的对象和suvivor的对象就是每次Yang
    GC后存货的对象,同时还可以看出每次Yang GC后进入老年代的对象大小,从而得到老年代的增长速率。
  • Full GC的触发频率和每次耗时:知道了老年代的增长速率就能计算出Full GC的触发频率;Full GC的每次耗时用FGCT/FGC得出;
  • 优化思路:尽量让Yang GC后存活的对象小于suvivor区的50%,这样都会留在年轻代里,尽量别让对象进入老年代,减少Full GC的频率;

内存泄漏是怎么回事:

在实际开发中,常常把一些数据存到JVM缓存中,比如map、List等,随着时间推移JVM缓存使用的越来越大,一直占用老年代的空间,导致频繁的Full GC,这就是一种内存泄漏;对于老旧数据没有及时清理导致一直占用着我们宝贵的内存资源,时间长了除了造成频繁的Full GC还有可能导致OOM;

这种情况完全可以考虑使用一些成熟的JVM级缓存框架来解决,比如ehcache等自带LUR数据淘汰算法的框架来作为JVM级的缓存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值