jvm调优工具

调优命令

        当我们的程序有卡顿,oom,死锁等等各种情况我们如果是自己写的代码,还能大概清楚出问题的代码在哪儿,如果你作为项目经理,具体业务代码并不清楚,要查找这些问题就需要jvm调优工具,当然也可以用以装逼。。。好了,那么我们一起来看下如何使用jvm的这些命令

前置条件,本地开启一个java项目,什么项目都可以

JPS

        jps命令语法:jps [options ] [ hostid ]   参数可以不写

        [options]参数 :
                -q:仅输出进程 id,不包括classname,jar name,arguments in main method
                -m:输出main method的参数
                -l:输出彻底的包名,应用主类名,jar的彻底路径名
                -v:输出jvm参数
                -V:输出经过flag文件传递到JVM中的参数(.hotspotrc文件或-XX:Flags=所指定的文件
                -Joption:传递参数到vm,例如:-J-Xms512m 

        [hostid]:
                [protocol:][[//]hostname][:port][/servername]web

                   协议             主机名     端口    服务名称     (可以远程查看其他服务器)

       使用:

               1. 打开cmd命令窗口,输入jps 查看当前运行的  java 进程ID 和 启动类的名称  

                        

                如果输入没有任何反应:(没问题的往下继续)

                        a.打开文件地址栏转到:%TMP% 

                        b.找到 hsperfdata_user(user是当前电脑登录用户名)文件夹

                        c. 右键属性》安全》找到对应用户,编辑》完全控制,应用后

 

                        d. 再运行java程序,看下里面有没有生成文件,又就可以再jps

                2. 其他参数自行测试

      ps: 这个参数最常用的作用应该是查询当前java进程id(本人使用如此)

JMAP

        jmap命令是一个可以输出所有内存中对象的工具,也可以将VM 中的heap,以二进制输出成文本。

        命令格式:

jmap [option] <pid>      (to connect to running process) 连接到正在运行的进程

jmap [option] <executable <core>    (to connect to a core file)     连接到核心文件

jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server) 连接到远程调试服务

1. -heap 打印heap的概要信息,GC使用的算法,heap(堆)的配置及JVM堆内存的使用情况

      例:  jmap -heap pid   (附翻译)

 2.  -finalizerinfo 打印正等候回收的对象的信息

        例:  jmap -finalizerinfo pid   (附翻译) 现在是0个待回收

 3.  -dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件

        例:jmap -dump:live,format=b,file=test.txt  pid

        输出到了当前目录(这个文件打开看不懂,后面我们用jvisualvm去分析就好了)

4.  -histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量。

        例:jmap -histo:live pid     (截图部分,太多了,一般不太会用这个查看,不好观察)

 ​​​

JSTACK

        jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息

        jstack [-F/-l/-m] pid      一般用来分析线程死锁和cpu高得情况

        -F:当正常输出的请求不被响应时,强制输出线程堆栈。

        -l:除堆栈外,会打印出额外的锁信息,在发生死锁时可以用jstack -l pid来观察锁持有情况  

        -m:如果调用到本地方法的话,可以显示C/C++的堆栈 

模拟线程死锁:

    private static Object a = new Object();
    private static Object b = new Object();

    public static void main(String[] args) {

        new Thread(() -> {
                synchronized (a){
                    try {
                        System.out.println("a对象上锁");
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (b){
                        System.out.println("b对象上锁");
                    }
                }
        }).start();

        new Thread(() -> {
            synchronized (b){
                try {
                    System.out.println("b线程对b对象上锁");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a){
                    System.out.println("b线程对a对象上锁");
                }
            }
        }).start();

    }

        出现死锁,先找到pid用  jstack -l pid查找死锁代码

找到一个死锁,对应得两个线程以及代码位置TestController.java:行数

 

        cpu高  只需要一个死循环让cpu不停执行计算就可以实现

        找到cpu使用最高得pid ,用jstack查找到对应线程,就能找到造成cpu高使用得代码

JSTAT

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

垃圾回收使用统计

       jstat -gc pid    1000指间隔一秒查询一次,后面按语法还可以指定查询次数

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

堆内存统计

        jstat -gccapacity pid

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数

新生代回收情况

        jstat -gcnew pid

S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
TT:对象在新生代存活的次数
MTT:对象在新生代存活的最大次数
DSS:期望的幸存区大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间

 老年代回收统计

        jstat -gcold pid

MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

元空间回收统计

       jstat -gcmetacapacity pid     

MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

堆空间百分比统计

        jstat -gcutil pid

S0:幸存1区当前使用比例
S1:幸存2区当前使用比例
E:伊甸园区使用比例
O:老年代使用比例
M:元数据区使用比例
CCS:压缩使用比例
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

jstat 是做jvm调优关键的命令,用这个命令我们可以分析出很多的问题:

年轻代对象增长的速率

       可以执行命令 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性能的影响。

        调优也不是一蹴而就的,不是说你查看了各种参数一顿分析然后调整参数就可以搞定了,这个是循序渐进的,再厉害的人也不可能一上来就能准确的说到问题点,分析只是找方向,经验越多,方向就会更正确,找问题就会更快速,实践才能检测真理

调优工具

      调优工具就是把上面的命令封装了,然后变成可视化的操作,方便我们使用

    jvisualvm

        java自带的一个jvm调优工具,使用也很简单,在自己的jdk安装目录里面就能找到

        jdk/bin/jvisualvm.exe打开就好

        默认监控本地所欲的java运行程序,其实应该就是上面的命令的可视化操作版本,也支持远程

 稍微看下:

随便点开一个java程序

 当前运行jvm参数,监控cpu,线程等等自行操作

还可以把我们前面导出的test.txt放入查看:

点击文件》装入》记得选择堆 Dump》选择文件打开

 可以看到,前面提到的文件就是查看当前的实例一些东西,查大量对象的时候经常使用,比如一个程序一直在创建对象,也没有回收,这个就能够找到是谁

ps: 一般不用 jmap 去查询 ,在监控里面点击 堆Dump

 
Arthas       

        阿里旗下的调优工具,没有使用过,据说挺好用,记录一下,以后可能会使用

        api:     https://alibaba.github.io/arthas

GC日志分析

        对于java应用我们可以通过一些配置把程序运行过程中的gc日志全部打印出来,然后分析gc日志得到关键性指标,分析 GC原因,调优JVM参数。
加入参数输出 gc日志( Tomcat则直接加在 JAVA_OPTS变量里
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:D:\gc-%t.log

 空的spring  boot项目,没啥特别多的内容稍微分析下一些东西

        上面的是参数,没啥特别的,基本都默认

1. 对于 1.349: 这是从jvm启动开始计算到这次GC经过的时间,还有具体的发生时间日期

2. Full GC 是一次full gc,括号里是gc的原因, PSYoungGen是年轻代的GC,ParOldGen是老年代的GC,Metaspace是元空间的GC(本次Full GC 原因就是元空间小了,默认21M不够)

3. 10226K -> 0K(73728K)  这三个数字分别对应GC之前分代占用的大小,GC之后分代占用,以及整个分代的大小。 young 年轻代   old 老年代   metaspace元空间
 
4. 0.0212227本次gc耗时
分析处理:
        
        可以看到元空间回收前大概21M ,回收了个寂寞,都没有内容被回收,所以jvm自动调大元空间,
        old区的值如何解释呢?
        1477K ->11146k(111104K) 11704K->11146k(184832K)
        
        也即GC前old区占用空间是1477K,GC后占用空间11146K,总空间111104K( 为啥回收后反而更大了?启动的时候,需要把很多spring boot 的内容存放到老年代,gc触发回收前没有多少内容,但是又不断的往老年代里面放,导致更多的数据进入,原来进入的数据其实大部分都没办法回收,因为spring boot 需要这些对象存活)
        堆GC前占用空间11704K,GC后占用空间11146K,总空间184832K
设置元空间大一点  -XX:MetaspaceSize=200M :现在就没有Full GC 了

 其他原因导致的Full GC就对应处理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值