【jvm系列-11】jvm性能调优篇---命令行工具的基本使用

刺骨的言语ヽ痛彻心扉 2024-03-22 15:42 72阅读 0赞

JVM系列整体栏目























































内容 链接地址
【一】初识虚拟机与java虚拟机 https://blog.csdn.net/zhenghuishengq/article/details/129544460
【二】jvm的类加载子系统以及jclasslib的基本使用 https://blog.csdn.net/zhenghuishengq/article/details/129610963
【三】运行时私有区域之虚拟机栈、程序计数器、本地方法栈 https://blog.csdn.net/zhenghuishengq/article/details/129684076
【四】运行时数据区共享区域之堆、逃逸分析 https://blog.csdn.net/zhenghuishengq/article/details/129796509
【五】运行时数据区共享区域之方法区、常量池 https://blog.csdn.net/zhenghuishengq/article/details/129958466
【六】对象实例化、内存布局和访问定位 https://blog.csdn.net/zhenghuishengq/article/details/130057210
【七】执行引擎,解释器、JIT即时编译器 https://blog.csdn.net/zhenghuishengq/article/details/130088553
【八】精通String字符串底层机制 https://blog.csdn.net/zhenghuishengq/article/details/130154453
【九】垃圾回收底层原理和算法以及JProfiler的基本使用 https://blog.csdn.net/zhenghuishengq/article/details/130261481
【十】垃圾回收器的种类以及内部的执行原理 https://blog.csdn.net/zhenghuishengq/article/details/130261481
【十一】jvm性能调优篇之命令行工具的基本使用 https://blog.csdn.net/zhenghuishengq/article/details/130641456

初始jvm调优命令行工具的基本使用

  • 一,JVM监控调优工具
    • 1,jvm调优篇概述
      • 1.1,生产环境中可能出现的问题
      • 1.2,性能优化的步骤
      • 1.3,性能测试指标
    • 2,JVM监控工具
      • 2.1,JPS
      • 2.2,jstat(重点)
      • 2.3,Jinfo
      • 2.4,jmap
      • 2.5,jhat
      • 2.6,jstack(重点)
      • 2.7,jcmd

一,JVM监控调优工具

从本文开始,正式进入jvm性能调优篇阶段

1,jvm调优篇概述

1.1,生产环境中可能出现的问题

  • 生产环境中发生了内存泄漏该如何处理?
  • 生产环境中该给服务器分配多少内存合适?
  • 如何对垃圾回收器的性能进行调优?
  • 生产环境中的CPU负载飙高该如何解决?
  • 生产环境应该给引用分配多少内存?
  • 不加日志,如何确定是否执行了某一行代码?
  • 不加日志,如何实时的查看某个方法的入参和返回值?

因此需要通过调优,来解决这些问题:防止出现OOM、解决OOM、减少Full GC出现的频率

1.2,性能优化的步骤

1,性能监控(发现问题): 通过一些监控工具来发现问题,然后对产生的问题的内容进行调优,一般可能出现的问题有:GC频繁、CPUload过高、OOM、内存泄漏、死锁、程序响应的时间较长等

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yKCtwiCa-1683872329325)(img/1682480566070.png)\]

2,性能分析(排查问题): 可以通过GCviewer堆日志进行分析;也可以运用一些命令行工具,如jstack、jmap、jinfo等分析;也可以通过dump出堆文件,然后使用内存分析工具分析文件;或者直接AkiraArthas,或者jconsoleJViaual来查看实时的JVM状态;或者直接使用jstack查看堆栈信息

3,性能调优(解决问题): 适当的增加内存;优化代码;增加机器;合理的设置线程池线程数量;增加消息中间件,缓存、消息队列等。

1.3,性能测试指标

主要从以下面的几个参数就行分析

1,停顿时间: 提交请求和返回请求响应之间的时间,一般比较关注的是平均时间

2,吞吐量: 单位时间内完成的工作量

3,并发数: 同一时刻,对服务器有实际交互的请求数

4,内存占用: Java堆区所占的内存大小

在调优中,停顿时间和吞吐量是主要关注点。

2,JVM监控工具

2.1,JPS

Java Process Status,表示java进程的状态。通过这个命令,可以显示指定系统内所有的HotSpot虚拟机进程,可用于查询正在运行的虚拟机线程。

  1. [root@VM-12-3-centos study]# jps
  2. 27586 Jps
  3. 29658 jar

除了使用这个jps查看,也可以在jps后面加上以下参数,分别是-l-q-m-v ,日常开发中,基本上很少用这些参数,当然,这些参数除了可以单独使用之外,还可以结合使用。

  1. //输出应用程序主类的全名称
  2. [root@VM-12-3-centos study]# jps -l
  3. 29658 study-0.0.1-SNAPSHOT.jar
  4. 30190 sun.tools.jps.Jps
  5. //只能够看到进程id
  6. [root@VM-12-3-centos study]# jps -q
  7. 29658
  8. 30190
  9. //查看进程id以及mian方法中参数信息
  10. [root@VM-12-3-centos study]# jps -m
  11. 29658 jar
  12. 3437 Jps -m
  13. //列出虚拟机启动时的JVM参数
  14. [root@VM-12-3-centos study]# jps -v
  15. 29658 study-0.0.1-SNAPSHOT.jar
  16. 30190 sun.tools.jps.Jps

随后也可以通过以下的命令查看对应的进程号的详情

  1. ps -ef | grep 29658
  2. ps -aux | grep 29658

如果在进程中,使用了 -XX:-UsePerfData 这个参数 ,那么jps将无法查看此进程的信息,同时也说明这个参数是默认开启的。

2.2,jstat(重点)

Jvm Statistics Monitoring Tool ,表示监视虚拟机运行状态的信息。他可以显示本地或者远程虚拟机进程中类加载、内存、垃圾收集、JIT即时编译等运行数据,常用于检测垃圾回收问题和内存泄漏问题。

输入这个jstat这个命令之后,可以查看到如何使用以及可加的参数。

  1. [root@VM-12-3-centos study]# jstat
  2. invalid argument count
  3. Usage: jstat -help|-options
  4. jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
  5. //<option> : 只要监视的对象信息,如class
  6. //-t:程序执行的总时间,显示在head头部的Timestamp中
  7. //-h:多少周期打印一次表头信息
  8. //<interval> : 周期,比如多长时间打印一次
  9. //<count> : 打印的总次数,默认只查询一次

这个option可以是很多选择的对象,这里以 class 为例,主要用于显示类加载的相关信息。这里表示的就是监视的是class对象,加了-t就会在前面通过这个Timestamp显示一个运行了多长时间,-h3表示每隔3个就会打印一次头部信息,接着的就是进程号,接着的就是1000 5,表示每间隔1秒打印一次,总共打印5次。

  1. [root@VM-12-3-centos study]# jstat -class -t -h3 29658 1000 5
  2. Timestamp Loaded Bytes Unloaded Bytes Time
  3. 6913.2 10912 19626.3 0 0.0 6.44
  4. 6914.3 10912 19626.3 0 0.0 6.44
  5. 6915.3 10912 19626.3 0 0.0 6.44
  6. Timestamp Loaded Bytes Unloaded Bytes Time
  7. 6916.3 10912 19626.3 0 0.0 6.44
  8. 6917.3 10912 19626.3 0 0.0 6.44

接下来对上面的各个参数做一个解释

  1. //Timestamp:程序运行的总时间
  2. //Loaded:加载的类的个数 //Bytes:加载类的字节数
  3. //Unloaded:卸载的类的个数 //Bytes:卸载的类的字节数
  4. //Time:加载的总体时间

上面这个option只用过了class,除了class之外,还可以使用垃圾回收相关的参数

  • -gc:显示GC相关的堆信息。如新生代老年代的容量、已用空间等
  • -gccapacity:显示的内容和-gc的基本一致,关注的内容是Java堆的各个区域使用到的最大、最小空间
  • -gcutil:显示内容与 -gc 基本一致,关注的内容是已使用空间占总空间的百分比
  • -gccause:与-gcutil功能一样,会额外输出导致最后一次或者当前发生gc的原因
  • -gcnew:显示新生代GC状况
  • -gcnewcapacity:与-gcnew基本相同,输出主要是关注使用到的最大空间和最小空间
  • -geold:显示老年代GC状况
  • -gcoldcapacity:显示的内容与-gcold基本相同,输出关注点是使用到的醉倒和最小空间
  • -gcpermcapacity:显示永久代使用到的最大空间

接下来可以通过这个命令分析GC相关的具体参数

  1. jstat -gc 进程号id
  2. [root@VM-12-3-centos ~]# jstat -gc 29658
  3. S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
  4. 1024.0 1024.0 0.0 480.3 15872.0 5225.1 80384.0 71941.6 59992.0 56814.9 7808.0 7262.8 3727 11.990 3 0.321 12.312

接下来对上面的参数做一个详细的解释

  1. //上面的S表示survivor区,C表示已经使用的容量,U表示已经使用的容量
  2. //E表示Eden区,O表示old区,M表示方法区,CCS表示压缩
  3. //YGC表示Young Gc次数,YGCT表示YoungGC时间
  4. //FGC表示Full Gc次数,FGCT表示Full GC时间
  5. //GCT表示总GC的时间,这里指的是Young GC和Full Gc的和

这些依旧可以和参数搭配使用

  1. jstat -gc -t -h3 29658 1000 5

其结果如下,和上面的class的现象是一样的

  1. [root@VM-12-3-centos ~]# jstat -gc -t -h3 29658 1000 5
  2. Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
  3. 86023.0 1024.0 1024.0 0.0 368.3 15872.0 6335.5 80384.0 71965.6 59992.0 56814.9 7808.0 7262.8 3753 12.074 3 0.321 12.395
  4. 86024.0 1024.0 1024.0 0.0 368.3 15872.0 6335.5 80384.0 71965.6 59992.0 56814.9 7808.0 7262.8 3753 12.074 3 0.321 12.395
  5. 86025.0 1024.0 1024.0 0.0 368.3 15872.0 6337.5 80384.0 71965.6 59992.0 56814.9 7808.0 7262.8 3753 12.074 3 0.321 12.395
  6. Timestamp S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
  7. 86026.0 1024.0 1024.0 0.0 368.3 15872.0 6337.5 80384.0 71965.6 59992.0 56814.9 7808.0 7262.8 3753 12.074 3 0.321 12.395
  8. 86027.0 1024.0 1024.0 0.0 368.3 15872.0 6339.6 80384.0 71965.6 59992.0 56814.9 7808.0 7262.8 3753 12.074 3 0.321 12.395

因此在实际开发中,可以通过这个jstat来对内存进行监控。并且可以通过这个Java进程启动的时间(TimeStamp)比上GC时间(GCT),得出GC时间占总运行时间的比例,如果该比例超过20%,则说明堆的压力较大,如果比例超过90%,则说明堆里面几乎没有可用空间,随时都可能出现OOM。

2.3,Jinfo

Configuration Info for Java,查看虚拟机配置参数的信息,也可以用于调整虚拟机的配置参数。在很多情况下,Java应用程序不会指定所有的Java虚拟机参数,开发人员可能不知道某一个具体参数的默认值,因此可以直接通过这个jinfo工具,开发人员可以很方便的找到java虚拟机参数的当前值。

jinfo的基本使用如下:

  1. [root@VM-12-3-centos study]# jinfo --help
  2. Usage:
  3. jinfo [option] <pid>
  4. (to connect to running process)
  5. jinfo [option] <executable <core>
  6. (to connect to a core file)
  7. jinfo [option] [server_id@]<remote server IP or hostname>
  8. (to connect to remote debug server)
  9. where <option> is one of:
  10. -flag <name> to print the value of the named VM flag
  11. -flag [+|-]<name> to enable or disable the named VM flag
  12. -flag <name>=<value> to set the named VM flag to the given value
  13. -flags to print VM flags
  14. -sysprops to print Java system properties
  15. <no option> to print both of the above
  16. -h | -help to print this help message

在jinfo中,主要可以用来查看和修改配置参数,查看的具体命令可以如下

  1. jinfo -flags PID //查看一些曾经赋值过的一些参数
  2. jinfo -flags 具体参数 PID //查看某个java进程的一些参数的值
  3. jinfo -flags UseParallel 29658 //查看当前进程是否用的是Parallel垃圾回收器

也可以修改参数,修改的具体命令如下

  1. jinfo -flag 具体参数 PID
  2. jinfo -flag PrintGcDetails 29658 //修改这个打印GC日志的状态,默认不打印,修改后为打印

2.4,jmap

JVM Memory Map,导出内存映像文件,显示内存的使用情况。 获取dump文件,同时也可以获取到Java进程的内存相关信息,如一些堆各个区域的使用情况,堆对象的统计信息,类加载信息等。可以通过jmap -help查看相关指令

  1. [root@VM-12-3-centos ~]# jmap -help
  2. Usage:
  3. jmap [option] <pid>(to connect to running process)
  4. jmap [option] <executable <core>(to connect to a core file)
  5. jmap [option] [server_id@]<remote server IP or hostname>(to connect to remote debug server)

jmap可以通过 -dump 生成Java堆转储快照dump文件,-dump:live只保存堆中存活的对象。可以通过该文件查看具体是什么原因导致的堆溢出,内存泄漏等问题。可以通过手动的方式生成,如下

  1. jmap -dump:format=b,file=zhs.hprof 29658
  2. jmap -dump:live:format=b,file=../zhs.hprof 29658 //只保存那一刻的存活对象

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cn7UOInD-1683872329326)(img/1682670110211.png)\]

也可以通过自动的方式生成,会在系统即将要OOM的时候自动生成一个dump文件,其指令如下

  1. -Xmx100m -XX:HeapDumpOnOutOfMemoryError -XX:heapDumpPath=zhs.hprof

可以通过 -heap 输出整个堆空间的详细信息,包括GC的使用,堆配置信息,以及内存的使用信息等,挺好用的

  1. jmap -heap 29658

可以通过 -histo 输出堆对象中的统计信息,包括一些类、实例数量和总容量等

  1. jmap -histo 29658
  2. jmap -histo:live 29658 //只统计堆中存活的对象

可以通过-finalizerinfo 显示需要被回收的对象信息,但是该命令只在Linux下有效

  1. jmap -finalizerinfo 29658

总结: 由于jmap将访问堆中的对象,为了保证在此过程中不被打扰,jmap需要在安全点机制访问,即stw的时候访问数据,因此可能导致对快照的分析结果存在偏差。并且如果一直无法stw,那么jmap将一直等下去。相对于jstat而言,jstat是实时监控的,这方面的准确度比jmap高。

2.5,jhat

JVM Heap Analysts Tool ,显而易见,从这几个单词来看就是用来分析堆内存的工具。在jmap这个工具中,可以生成一个dump文件,而这个jhat就是为了配合这个jmap使用的。在jhat中,其内部内嵌了一个小型的服务器,生成dump文件的分析结果之后,用户可以在浏览器中查看分析结果。

使用了这个jhat命令,就相当于启动了一个http服务,访问地址为:localhost:7000。但是在JDK9,10之后已被删除,官方建议使用VisualVM图形化界面代替这个命令行工具。

2.6,jstack(重点)

jmap是用于打印堆空间的快照,这个jstack就是用于打印这个栈帧中的快照了。JVM StackTrace ,用于生成指定进程当前时刻的线程快照。线程快照指的就是虚拟机内每一条线程正在执行方法堆栈的集合。

生成线程快照的主要作用如下:可以用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致长时间等待,阻塞等问题。

其基本语法如下:

  1. [root@VM-12-3-centos study]# jstack
  2. Usage:
  3. jstack [-l] <pid>(to connect to running process)
  4. jstack -F [-m] [-l] <pid>(to connect to a hung process)
  5. jstack [-m] [-l] <executable> <core>(to connect to a core file)
  6. jstack [-m] [-l] [server_id@]<remote server IP or hostname>(to connect to a remote debug server)
  7. Options:
  8. -F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
  9. -m to print both java and native frames (mixed mode)
  10. -l long listing. Prints additional information about locks
  11. -h or -help to print this help message

接下来继续体验一下,直接通过这个jstack -pid

  1. [root@VM-12-3-centos study]# jstack 29658
  2. "C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f0790141000 nid=0x73e3 waiting on condition [0x0000000000000000]
  3. java.lang.Thread.State: RUNNABLE
  4. "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f079013e000 nid=0x73e2 waiting on condition [0x0000000000000000]
  5. java.lang.Thread.State: RUNNABLE
  6. ...

如下面模拟一段死锁问题,其代码如下

  1. package com.zhs.study.test;
  2. /**
  3. * @author zhenghuisheng
  4. * @date : 2023/5/12
  5. */
  6. public class DeadBlockTest {
  7. public static void main(String[] args) {
  8. StringBuilder s1 = new StringBuilder();
  9. StringBuilder s2= new StringBuilder();
  10. new Thread(){
  11. @Override
  12. public void run() {
  13. synchronized (s1){
  14. s1.append("a");
  15. s1.append("b");
  16. try {
  17. Thread.sleep(10000);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. synchronized (s2){
  22. s2.append("c");
  23. s2.append("d");
  24. }
  25. }
  26. }
  27. }.start();
  28. new Thread(){
  29. @Override
  30. public void run() {
  31. synchronized (s2){
  32. s1.append("a");
  33. s1.append("b");
  34. try {
  35. Thread.sleep(10000);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. synchronized (s1){
  40. s2.append("c");
  41. s2.append("d");
  42. }
  43. }
  44. }
  45. }.start();
  46. }
  47. }

运行上面的这一段代码之后,再通过jps获取其jvm对应的进程号,再通过jstack + 进程号即可,如我这边通过输入jps查到的进程号为12308,然后直接 jstack 12308 即可获取死锁等问题。

  1. Found one Java-level deadlock:
  2. =============================
  3. "Thread-1":
  4. waiting to lock monitor 0x000000001ce7f608 (object 0x000000076b5b6a28, a java.lang.StringBuilder),
  5. which is held by "Thread-0"
  6. "Thread-0":
  7. waiting to lock monitor 0x000000001ce7cd78 (object 0x000000076b5b6a70, a java.lang.StringBuilder),
  8. which is held by "Thread-1"
  9. Java stack information for the threads listed above:
  10. ===================================================
  11. "Thread-1":
  12. at com.zhs.study.test.DeadBlockTest$2.run(DeadBlockTest.java:42)
  13. - waiting to lock <0x000000076b5b6a28> (a java.lang.StringBuilder)
  14. - locked <0x000000076b5b6a70> (a java.lang.StringBuilder)
  15. "Thread-0":
  16. at com.zhs.study.test.DeadBlockTest$1.run(DeadBlockTest.java:23)
  17. - waiting to lock <0x000000076b5b6a70> (a java.lang.StringBuilder)
  18. - locked <0x000000076b5b6a28> (a java.lang.StringBuilder)
  19. Found 1 deadlock.

\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vn3KpHau-1683872329327)(img/1683866732686.png)\]

除了死锁,其他的sleep长时间等待等等,也能查看得到。

2.7,jcmd

在jdk7之后,新增了一个命令行工具jcmd,可以实现除了jstat之外的所有命令,如导出堆、内存使用、查看java进程、导出线程信息、执行GC、jvm运行时间等。并且更加推荐jcmd代替jmap的使用。

可以通过 jcmd -l 获取所有的jvm的进程,jcmd pid help 查看指定线程所支持的所有命令,可以通过这个jcmd pid 具体命令 来显示指定进程的指令命令的数据。

可以先通过这个help命令查看有哪些命令可以执行

  1. jcmd 1744 help

其执行结果如下

  1. C:\Users\p'v>jcmd 1744 help
  2. 1744:
  3. The following commands are available:
  4. JFR.stop
  5. JFR.start
  6. JFR.dump
  7. JFR.check
  8. VM.native_memory
  9. VM.check_commercial_features
  10. VM.unlock_commercial_features
  11. ManagementAgent.stop
  12. ManagementAgent.start_local
  13. ManagementAgent.start
  14. GC.rotate_log
  15. Thread.print
  16. GC.class_stats
  17. GC.class_histogram
  18. GC.heap_dump
  19. GC.run_finalization
  20. GC.run
  21. VM.uptime
  22. VM.flags
  23. VM.system_properties
  24. VM.command_line
  25. VM.version
  26. help

再通过查出来的命令来查看对应命令的数据

  1. // 打印线程信息
  2. jcmd 1744 Thread.print
  3. // 查看gc的内存信息
  4. jcmd 1744 GC.class_histogram
  5. // 生成GC的dump文件
  6. jcmd 1744 GC.heap_dump d:\\a.hprof
  7. ...

发表评论

表情:
评论列表 (有 0 条评论,72人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JVM性能工具

    一、JDK工具 先来看看有哪些常用的工具可以辅助我们进行性能调优和问题排查,后面再通过一个具体的示例结合工具来分析调优。 1、JDK工具 JDK自带了很多性能监控