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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【jvm系列-11】jvm性能调优篇---命令行工具的基本使用

一,JVM监控调优工具

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

1,jvm调优篇概述

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

生产环境中发生了内存泄漏该如何处理?

生产环境中该给服务器分配多少内存合适?

如何对垃圾回收器的性能进行调优?

生产环境中的CPU负载飙高该如何解决?

生产环境应该给引用分配多少内存?

不加日志,如何确定是否执行了某一行代码?

不加日志,如何实时的查看某个方法的入参和返回值?

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


1.2,性能优化的步骤

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


43e87e6fdf694dd586f5c3cc05167fe8.png


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


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


1.3,性能测试指标

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


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


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


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


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


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


2,JVM监控工具

2.1,JPS

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

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

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

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

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

ps -ef | grep 29658
ps -aux | grep 29658

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


2.2,jstat(重点)

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


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

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

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


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

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


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

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


-gc:显示GC相关的堆信息。如新生代老年代的容量、已用空间等

-gccapacity:显示的内容和-gc的基本一致,关注的内容是Java堆的各个区域使用到的最大、最小空间

-gcutil:显示内容与 -gc 基本一致,关注的内容是已使用空间占总空间的百分比

-gccause:与-gcutil功能一样,会额外输出导致最后一次或者当前发生gc的原因

-gcnew:显示新生代GC状况

-gcnewcapacity:与-gcnew基本相同,输出主要是关注使用到的最大空间和最小空间

-geold:显示老年代GC状况

-gcoldcapacity:显示的内容与-gcold基本相同,输出关注点是使用到的醉倒和最小空间

-gcpermcapacity:显示永久代使用到的最大空间

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

jstat -gc 进程号id
[root@VM-12-3-centos ~]# jstat -gc 29658
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
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

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

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

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

jstat -gc -t -h3 29658 1000 5

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

[root@VM-12-3-centos ~]# jstat -gc -t -h3 29658 1000 5
Timestamp        S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
        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
        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
        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
Timestamp        S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
        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
        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的基本使用如下:

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

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

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

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

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

2.4,jmap

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

[root@VM-12-3-centos ~]# jmap -help
Usage:
    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)

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

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

b274baa4352b498b9c1521c184430dc7.png

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

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

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

jmap -heap 29658

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

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

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

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 ,用于生成指定进程当前时刻的线程快照。线程快照指的就是虚拟机内每一条线程正在执行方法堆栈的集合。


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


其基本语法如下:

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

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

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

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

package com.zhs.study.test;
/**
 * @author zhenghuisheng
 * @date : 2023/5/12
 */
public class DeadBlockTest {
    public static void main(String[] args) {
        StringBuilder s1 = new StringBuilder();
        StringBuilder s2= new StringBuilder();
        new Thread(){
            @Override
            public void run() {
                synchronized (s1){
                    s1.append("a");
                    s1.append("b");
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s2){
                        s2.append("c");
                        s2.append("d");
                    }
                }
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                synchronized (s2){
                    s1.append("a");
                    s1.append("b");
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s1){
                        s2.append("c");
                        s2.append("d");
                    }
                }
            }
        }.start();
    }
}

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

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000001ce7f608 (object 0x000000076b5b6a28, a java.lang.StringBuilder),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000001ce7cd78 (object 0x000000076b5b6a70, a java.lang.StringBuilder),
  which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
        at com.zhs.study.test.DeadBlockTest$2.run(DeadBlockTest.java:42)
        - waiting to lock <0x000000076b5b6a28> (a java.lang.StringBuilder)
        - locked <0x000000076b5b6a70> (a java.lang.StringBuilder)
"Thread-0":
        at com.zhs.study.test.DeadBlockTest$1.run(DeadBlockTest.java:23)
        - waiting to lock <0x000000076b5b6a70> (a java.lang.StringBuilder)
        - locked <0x000000076b5b6a28> (a java.lang.StringBuilder)
Found 1 deadlock.

92d39b15f371421d92e3e27b1ae73538.png


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


2.7,jcmd

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


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


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

jcmd 1744 help

其执行结果如下

C:\Users\p'v>jcmd 1744 help
1744:
The following commands are available:
JFR.stop
JFR.start
JFR.dump
JFR.check
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
GC.rotate_log
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.run_finalization
GC.run
VM.uptime
VM.flags
VM.system_properties
VM.command_line
VM.version
help

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

// 打印线程信息
jcmd 1744 Thread.print
// 查看gc的内存信息
jcmd 1744 GC.class_histogram
// 生成GC的dump文件
jcmd 1744 GC.heap_dump d:\\a.hprof
...
相关文章
|
3月前
|
存储 监控 算法
jvm-性能调优(二)
jvm-性能调优(二)
|
23天前
|
监控 架构师 Java
Java虚拟机调优的艺术:从入门到精通####
本文作为一篇深入浅出的技术指南,旨在为Java开发者揭示JVM调优的神秘面纱,通过剖析其背后的原理、分享实战经验与最佳实践,引领读者踏上从调优新手到高手的进阶之路。不同于传统的摘要概述,本文将以一场虚拟的对话形式,模拟一位经验丰富的架构师向初学者传授JVM调优的心法,激发学习兴趣,同时概括性地介绍文章将探讨的核心议题——性能监控、垃圾回收优化、内存管理及常见问题解决策略。 ####
|
1月前
|
监控 Java 编译器
Java虚拟机调优指南####
本文深入探讨了Java虚拟机(JVM)调优的精髓,从内存管理、垃圾回收到性能监控等多个维度出发,为开发者提供了一系列实用的调优策略。通过优化配置与参数调整,旨在帮助读者提升Java应用的运行效率和稳定性,确保其在高并发、大数据量场景下依然能够保持高效运作。 ####
32 1
|
1月前
|
存储 算法 Java
JVM进阶调优系列(10)敢向stop the world喊卡的G1垃圾回收器 | 有必要讲透
本文详细介绍了G1垃圾回收器的背景、核心原理及其回收过程。G1,即Garbage First,旨在通过将堆内存划分为多个Region来实现低延时的垃圾回收,每个Region可以根据其垃圾回收的价值被优先回收。文章还探讨了G1的Young GC、Mixed GC以及Full GC的具体流程,并列出了G1回收器的核心参数配置,帮助读者更好地理解和优化G1的使用。
|
1月前
|
监控 Java 测试技术
Elasticsearch集群JVM调优垃圾回收器的选择
Elasticsearch集群JVM调优垃圾回收器的选择
55 1
|
1月前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
1月前
|
监控 Java 编译器
Java虚拟机调优实战指南####
本文深入探讨了Java虚拟机(JVM)的调优策略,旨在帮助开发者和系统管理员通过具体、实用的技巧提升Java应用的性能与稳定性。不同于传统摘要的概括性描述,本文摘要将直接列出五大核心调优要点,为读者提供快速预览: 1. **初始堆内存设置**:合理配置-Xms和-Xmx参数,避免频繁的内存分配与回收。 2. **垃圾收集器选择**:根据应用特性选择合适的GC策略,如G1 GC、ZGC等。 3. **线程优化**:调整线程栈大小及并发线程数,平衡资源利用率与响应速度。 4. **JIT编译器优化**:利用-XX:CompileThreshold等参数优化即时编译性能。 5. **监控与诊断工
|
1月前
|
存储 监控 Java
JVM进阶调优系列(8)如何手把手,逐行教她看懂GC日志?| IT男的专属浪漫
本文介绍了如何通过JVM参数打印GC日志,并通过示例代码展示了频繁YGC和FGC的场景。文章首先讲解了常见的GC日志参数,如`-XX:+PrintGCDetails`、`-XX:+PrintGCDateStamps`等,然后通过具体的JVM参数和代码示例,模拟了不同内存分配情况下的GC行为。最后,详细解析了GC日志的内容,帮助读者理解GC的执行过程和GC处理机制。
|
2月前
|
Arthas 监控 数据可视化
JVM进阶调优系列(7)JVM调优监控必备命令、工具集合|实用干货
本文介绍了JVM调优监控命令及其应用,包括JDK自带工具如jps、jinfo、jstat、jstack、jmap、jhat等,以及第三方工具如Arthas、GCeasy、MAT、GCViewer等。通过这些工具,可以有效监控和优化JVM性能,解决内存泄漏、线程死锁等问题,提高系统稳定性。文章还提供了详细的命令示例和应用场景,帮助读者更好地理解和使用这些工具。
|
2月前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。