从 JVM 到全链路:高并发系统性能监控与调优实战指南

25 年 12 月 7 日 星期日
2910 字
15 分钟

摘要

本文从 JVM 内存模型与 GC 机制切入,梳理中间件、数据库、压测报表等关键监控指标,演示 jvisualvm、jconsole 及 jstat/jmap/jstack 等工具的使用方法,给出可落地的 JVM 调优参数与排查思路,帮助读者建立高并发系统性能监控与问题定位的完整方法论。

1. JVM 内存模型

  • 程序计数器 Program Counter Register:
    • 记录的是正在执行的虚拟机字节码指令的地址,
    • 此内存区域是唯一一个在JAVA虚拟机规范中没有规定任何OutOfMemoryError的区域
  • 虚拟机:VM Stack
    • 描述的是 JAVA 方法执行的内存模型,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法接口等信息
    • 局部变量表存储了编译期可知的各种基本数据类型、对象引用
    • 线程请求的栈深度不够会报 StackOverflowError 异常
    • 栈动态扩展的容量不够会报 OutOfMemoryError 异常
    • 虚拟机栈是线程隔离的,即每个线程都有自己独立的虚拟机栈
  • 本地方法:Native Stack
    • 本地方法栈类似于虚拟机栈,只不过本地方法栈使用的是本地方法
  • 堆:Heap
    • 几乎所有的对象实例都在堆上分配内存

2. 堆

所有的对象实例以及数组都要在堆上分配。堆是垃圾收集器管理的主要区域,也被称为“GC堆”;也是我们优化最多考虑的地方。
堆可以细分为:

  • 新生代
    • Eden 空间
    • From Survivor 空间
    • To Survivor 空间
  • 老年代
  • 永久代/元空间
    • Java8 以前永久代,受 jvm 管理,java8 以后元空间,直接使用物理内存。因此,默认情况下,元空间的大小仅受本地内存限制。

垃圾回收 从 Java8 开始,HotSpot 已经完全将永久代(Permanent Generation)移除,取而代之的是一个新的区域—元空间(MetaSpace)

3. jconsole 与 jvisualvm

Jdk 的两个小工具 jconsole、jvisualvm(升级版的 jconsole);通过命令行启动,可监控本地和远程应用。远程应用需要配置。

3.1 jvisualvm 能干什么

监控内存泄露,跟踪垃圾回收,执行时内存、cpu 分析,线程分析...
运行:正在运行的
休眠:sleep
等待:wait
驻留:线程池里面的空闲线程
监视:阻塞的线程,正在等待锁

3.2 安装插件方便查看 gc

  • Cmd 启动 jvisualvm
  • 工具->插件
  • 如果 503 错误解决:

4. 监控指标

4.1 中间件指标

常用的中间件例如Tomcat, Weblogic等指标主要包括JVM,ThreadPool,JDBC具体如下:

一级指标 二级指标 单位 解释
GC GC频率 每秒多少次 java虚拟机垃圾部分回收频率
Full GC频率 每小时多少次 java虚拟机垃圾部分回收频率
Full GC平均时长 用于垃圾完全回收的平均时长
Full GC最大时长 用于垃圾回收的最大时长
堆使用频率 堆使用率
ThreadPool Active Thread Count 活动的线程数
Pending User Request 处于排队的用户请求个数
JDBC JDBC Active Collection JDBC活动连接数
  • 当前正在运行的线程数不能超过设定的最大值。一般情况下系统性能较好的情况下,线程数最小值设置 50 和最大值设置 200 比较合适。
  • 当前运行的 JDBC 连接数不能超过设定的最大值。一般情况下系统性能较好的情况下,JDBC 最小值设置 50 和最大值设置 200 比较合适。
  • GC频率不能频繁,特别是 FULL GC 更不能频繁,一般情况下系统性能较好的情况下,JVM 最小堆大小和最大堆大小分别设置 1024M 比较合适。

4.2 数据库指标

常用的数据库例如MySql指标主要包括SQL、吞吐量、缓存命中率、连接数等,具体如下:

一级指标 二级指标 单位 解释
SQL 耗时 微秒 执行SQL耗时
吞吐量 QPS 每秒查询次数
TPS 每秒事务次数
命中率 Key Buffer命中率 百分之 索引缓冲区命中率
InnoDB Buffer命中率 百分之 InnoDB缓冲区命中率
Query Cache命中率 百分之 查询缓存命中率
Table Cache命中率 百分之 表缓存命中率
Thread Cache命中率 百分之 线程缓存命中率
等待次数 锁等待次数
等待时间 微秒 锁等待时间
  • SQL 耗时越小越好,一般情况下微秒级别。
  • 命中率越高越好,一般情况下不能低于 95%。
  • 锁等待次数越低越好,等待时间越短越好。
压测内容压测线程数吞吐量/s90%响应时间99%响应时间
Nginx50233511944
Gateway5010367831
简单服务5011341817
首页一级菜单渲染50270(db,thymeleaf)267365
首页渲染(开缓存)50290251365
首页渲染(开缓存、 优化数据库、关日 志)50700105183
三级分类数据获取502(db)/8(加索引)......
三级分类(优化业 务)50111571896
三 级 分 类 ( 使 用 redis 作为缓存)50411153217
首页全量数据获取507(静态资源)
Nginx+Gateway50
Gateway+简单服务50312630125
全链路5080088310
  • 中间件越多,性能损失越大,大多都损失在网络交互了;
  • 业务:
    • Db(MySQL 优化)
    • 模板的渲染速度(缓存)
    • 静态资源

5. JVM 分析&调优

JVM调优,调的是稳定,并不能带给你性能的大幅提升。服务稳定的重要性就不用多说了,保证服务的稳定,gc 永远会是 Java 程序员需要考虑的不稳定因素之一。复杂和高并发下的服务,必须保证每次 gc 不会出现性能下降,各种性能指标不会出现波动,gc 回收规律而且干净,找到合适的 jvm 设置。Full gc 最会影响性能,根据代码问题,避免full gc 频率。可以适当调大年轻代容量,让大对象可以在年轻代触发 yong gc,调整大对象在年轻代的回收频次,尽可能保证大对象在年轻代回收,减小老年代缩短回收时间;

5.1 几个常用工具

命令功能描述
jstack查看 jvm 线程运行状态,是否有死锁现象等等信息
jinfo可以输出并修改运行时的 java 进程的 opts。
jps与 unix 上的 ps 类似,用来显示本地的 java 进程,可以查看本地运行着几个java程序,并显示他们的进程号。
jstat一个极强的监视 VM 内存工具。可以用来监视 VM 内存内的各种堆和非堆的大小及其内存使用量。
jmap打印出某个 java 进程(使用 pid)内存内的所有'对象'的情况(如:产生那些对象,及其数量)

5.2 命令示例

jstat 工具特别强大,有众多的可选项,详细查看堆内各个部分的使用量,以及加载类的数量。使用时,需加上查看进程的进程 id,和所选参数。
jstat -class pid 显示加载 class 的数量,及所占空间等信息
jstat -compiler pid 显示 VM 实时编译的数量等信息。
jstat -gc pid 可以显示 gc 的信息,查看 gc 的次数,及时间
jstat -gccapacity pid 堆内存统计,三代(young,old,perm)内存使用和占用大小
jstat -gcnew pid 新生代垃圾回收统计
jstat -gcnewcapacity pid 新生代内存统计
jstat -gcold pid 老年代垃圾回收统计
除了以上一个参数外,还可以同时加上 两个数字,如:jstat -printcompilation 3024 250 6 是每 250 毫秒打印一次,一共打印 6 次,还可以加上-h3 每三行显示一下标题。
jstat -gcutil pid 1000 100 : 1000ms 统计一次 gc 情况统计 100 次;
在使用这些工具前,先用 JPS 命令获取当前的每个 JVM 进程号,然后选择要查看的JVM。
jinfo 是 JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括JavaSystem 属性和 JVM 命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo 可以从 core 文件里面知道崩溃的 Java 应用程序的配置信息
jinfo pid 输出当前 jvm 进程的全部参数和系统属性
jinfo -flag name pid 可以查看指定的 jvm 参数的值;打印结果:-无此参数,+有
jinfo -flag [+|-]name pid 开启或者关闭对应名称的参数(无需重启虚拟机)
jinfo -flag name=value pid 修改指定参数的值
jinfo -flags pid 输出全部的参数
jinfo -sysprops pid 输出当前 jvm 进行的全部的系统属性
jmap 可以生成 heap dump 文件,也可以查看堆内对象分析内存信息等,如果不使用这个命令,还可以使用-XX:+HeapDumpOnOutOfMemoryError 参数来让虚拟机出现OOM的时候自动生成 dump 文件。
jmap -dump:live,format=b,file=dump.hprof pid dump 堆到文件,format 指定输出格式,live 指明是活着的对象,file 指定文件名。eclipse 可以打开这个文件
jmap -heap pid 打印 heap 的概要信息,GC 使用的算法,heap 的配置和使用情况,可以用此来判断内存目前的使用情况以及垃圾回收情况
jmap -finalizerinfo pid 打印等待回收的对象信息
jmap -histo:live pid 打印堆的对象统计,包括对象数、内存大小等。jmap -histo:live这个命令执行,JVM 会先触发 gc,然后再统计信息
jmap -clstats pid 打印 Java 类加载器的智能统计信息,对于每个类加载器而言,对于每个类加载器而言,它的名称,活跃度,地址,父类加载器,它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。
jmap -F -histo pid -F 强制模式。如果指定的 pid 没有响应,请使用 jmap -dump 或jmap -histo 选项。此模式下,不支持 live 子选项。
jstack 是 jdk 自带的线程堆栈分析工具,使用该命令可以查看或导出 Java 应用程序中线程堆栈信息。
jstack pid 输出当前 jvm 进程的全部参数和系统属性

5.3 调优项

官方文档

文章标题:从 JVM 到全链路:高并发系统性能监控与调优实战指南

文章作者:子木

文章链接:https://blog.zimutool.cn/posts/architecture/performance-monitor[复制]

最后修改时间:


商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,您可以自由地在任何媒体以任何形式复制和分发作品,也可以修改和创作,但是分发衍生作品时必须采用相同的许可协议。
本文采用CC BY-NC-SA 4.0进行许可。