JVM:性能监控工具分析和线上问题排查实践

前言

在日常开发过程中,多少都会碰到一些jvm相关的问题,比如:内存溢出、内存泄漏、cpu利用率飙升到100%、线程死锁、应用异常宕机等。
在这个日益内卷的环境,如何运用好工具分析jvm问题,成为每个java攻城狮必备的技能。所以白梦特意整理了jdk自带分析工具的使用,以及常见的jvm问题分析和处理

一、jdk自带分析工具介绍

jps(Java Process Status):

用途: 用于列出当前系统中所有正在运行的 Java 进程,并显示其进程ID以及启动时的类名或 JAR 文件。

基本用法:

jps [ options ] [ hostid ]

常见选项:

  • -q: 仅显示进程ID,不显示类名或 JAR 文件。
  • -m: 输出主类的全名,如果进程是通过 main 方法启动的话。
  • -l: 输出主类或 JAR 文件的全路径名。
  • -v: 输出传递给 JVM 的参数。

比如输出所有正在运行的进程:
image.png

jstat(JVM Statistics Monitoring Tool):

用途: 用于监控 JVM 的各种统计信息,如堆内存使用、垃圾回收、类加载等。

基本用法:

jstat [ options ] <vmid> [ interval [ count ]]
  • <vmid>:Java 虚拟机的进程ID,可以使用 jps 命令查看。
  • interval:采样时间间隔(以毫秒为单位)。
  • count:采样次数,表示统计信息将被输出的次数。

常见选项:

  • -class:显示类加载器统计信息。
  • -compiler:显示即时编译器统计信息。
  • -gc:显示垃圾回收统计信息,包括各代的统计信息和总的垃圾回
  • -gccapacity:显示堆内存各区域的容量统计信息。
  • -gccause:显示垃圾回收的原因统计信息。
  • -gcmetacapacity:显示元空间和压缩类空间的统计信息。
  • -gcnew:显示新生代垃圾回收统计信息。
  • -gcnewcapacity:显示新生代堆内存各区域的容量统计信息。
  • -gcold:显示老年代垃圾回收统计信息。
  • -gcoldcapacity:显示老年代堆内存各区域的容量统计信息。
  • -gcutil:显示各代垃圾回收的百分比信息。
  • -printcompilation:显示已经被编译的方法的信息。

比如想看垃圾回收统计数据,执行jstat -gc 7499命令
image.png
对应结果字段:

  • S0C: Survivor 0(S0)容量(以千字节为单位) - 用于对象的Survivor Space 0的总空间。
  • S1C: Survivor 1(S1)容量(以千字节为单位) - 用于对象的Survivor Space 1的总空间。
  • S0U: Survivor 0(S0)使用量(以千字节为单位) - 当前由对象使用的Survivor Space 0的量。
  • S1U: Survivor 1(S1)使用量(以千字节为单位) - 当前由对象使用的Survivor Space 1的量。
  • EC: Eden空间容量(以千字节为单位) - 用于对象的Eden空间的总空间。
  • EU: Eden空间使用量(以千字节为单位) - 当前由对象使用的Eden空间的量。
  • OC: 老年代空间容量(以千字节为单位) - 用于对象的老年代的总空间。
  • OU: 老年代空间使用量(以千字节为单位) - 当前由对象使用的老年代的量。
  • MC: 元空间容量(以千字节为单位) - Metaspace的总容量(用于存储类元数据的非堆内存)。
  • MU: 元空间使用量(以千字节为单位) - 当前由对象使用的Metaspace的量。
  • CCSC: 压缩类空间容量(以千字节为单位) - 压缩类空间的总容量。
  • CCSU: 压缩类空间使用量(以千字节为单位) - 当前由对象使用的压缩类空间的量。
  • YGC: Young代垃圾回收次数 - Young代被垃圾回收的次数。
  • YGCT: Young代垃圾回收时间(以秒为单位) - 在Young代垃圾回收上花费的总时间。
  • FGC: Full垃圾回收次数 - 发生Full(老年代)垃圾回收的次数。
  • FGCT: Full垃圾回收时间(以秒为单位) - 在Full垃圾回收上花费的总时间。
  • CGC: 编译次数 - JVM被要求编译方法的次数。
  • CGCT: 编译时间(以秒为单位) - 在编译上花费的总时间。
  • GCT: 总垃圾回收时间(以秒为单位) - Young和Full垃圾回收时间的总和。

jinfo(Java Configuration Info):

用途: 用于查看和调整 JVM 的配置信息,包括系统属性、JVM 参数等。

基本用法:

jinfo [ options ] <pid>
  • <pid>:Java 进程的进程ID,可以使用 jps 命令查看。

常见选项:

  • -flags:显示虚拟机启动时的命令行标志和系统属性。
  • -sysprops:显示 Java 系统属性的值。
  • <name>:显示指定名称的系统属性或 JVM 参数的值。

假如想看某个jar启动时用到的哪些参数,可以执行jinfo -flags xxxx
image.png

jmap(Java Memory Map):

用途: 用于生成堆转储快照,以便分析 Java 进程的内存使用情况。

基本用法:

jmap [ options ] <pid>
  • <pid>:Java 进程的进程ID,可以使用 jps 命令查看。

常见选项:

  • -clstats
    连接到运行中的 Java 进程并打印类加载器的统计信息。该选项显示有关类加载器及其加载的类的信息。

  • -finalizerinfo:连接到运行中的 Java 进程并打印等待终结的对象信息。这会显示等待执行 finalize() 方法的对象的数量和相关信息。

  • -histo[:[<histo-options>]]:连接到运行中的 Java 进程并打印 Java 对象堆的直方图(histogram)。该选项显示不同类型对象的数量和占用的内存。

    • histo-options可选参数包括:
      • live: 仅统计活动对象。
      • all: 统计所有对象。
      • file=<file>: 将直方图数据写入指定文件。
      • parallel=<number>: 用于堆检查的并行线程数。
  • -dump:<dump-options>:连接到运行中的 Java 进程并生成 Java 堆的转储(dump)。可以选择性地指定转储选项,例如 live 以仅转储活动对象,all 以转储整个堆。

    • dump-options可选参数包括:
      • live: 仅转储活动对象。
      • all: 转储整个堆。
      • format=b: 以二进制格式转储。
      • file=<file>: 将转储数据写入指定文件。
      • gz=<number>: 如果指定,以给定的压缩级别以 gzip 格式进行转储

比如发生oom后要导出dump文件,执行jmap -dump:all,format=b,file=heapdump.bin 123456,其中123456是进程pid。生成的文件拖进visualVM进行下一步分析。参考下面讲到的内存溢出排查

image.png

jstack(Java Stack Trace):

用途: 用于生成线程转储快照,以便分析 Java 进程中的线程状态和堆栈信息。

基本用法:

jstack [ options ] <pid>
  • <pid>:Java 进程的进程ID,可以使用 jps 命令查看。

常见选项:

  • -l:长格式选项主要关注锁相关的信息,包括线程所拥有的锁以及线程等待的锁。对于识别锁竞争和死锁等问题非常有用。
  • -e:拓展格式选项显示线程的更多信息,包括线程的状态、等待条件、锁的拥有者等。它的焦点更广泛,包括线程的整体状态和执行信息。

二、问题排查

1、内存溢出排查

内存溢出常见的原因之一是内存泄漏。当应用程序中的对象不再被引用,但仍然占用内存时,就会发生内存泄漏。如果这种情况发生得足够频繁,最终会导致整个堆内存耗尽。
还有其他原因例如:不恰当的缓存使用、限递归或循环、大对象、过度使用线程、JVM参数配置堆内存设置太小等。下面模拟内存溢出问题的排查。
首先写一个简单的内存溢出的springboot代码。

@GetMapping("/demo3")
public void demo3() {List<CustomObj> list = new ArrayList<>();while (true) {list.add(new CustomObj());}
}public class CustomObj{}

启动项目时,加上如下代码,让程序内存溢出时生成日志

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dumpfile

当然,如果项目没有配置HeapDumpOnOutOfMemoryError,也可先用jps查看java进程pid,再使用jmap手动导出。

jmap -dump:all,format=b,file=heapdump.bin 123456

使用jmap(特别是线上)时的注意事项:

  1. 生成堆转储文件可能对应用程序的性能产生一些短暂的影响,因为在生成转储文件时,Java 虚拟机会停止应用程序的执行一小段时间。
  2. 堆转储文件可能非常大,因此在生成之前,请确保有足够的磁盘空间。

启动项目后,访问controller方法,程序会抛出java.lang.OutOfMemoryError: Java heap space异常,并生成hropf文件。
image.png
导入到visualVM工具分析,VisualVM是一款由 Oracle 提供的免费的、功能强大的 Java 虚拟机监控、故障诊断和性能调优工具。它为开发者提供了一个集成的可视化界面,用于监控和分析 Java 应用程序的运行时行为。
图中可以看出CustomObj对象数量占比高达93.3%,并且类大小占到了68.3%。
image.png

左上角选择Threads,展开红色实例,找到出现异常的代码位置
image.png
同理,内存泄漏的的排查也可以使用以上的方式。

2、CPU利用率飙升到100%

CPU100%有以下常见原因导致的:

  • 无限循环或死循环: 如果程序中存在无限循环或者死循环,会导致程序一直在执行某个循环,使得CPU占用率保持在高水平。
  • 线程问题: 多线程程序中,如果线程没有正确地被管理或者存在死锁,会导致CPU资源被不断占用。
  • 内存泄漏: 如果程序中存在内存泄漏,即无用的对象无法被垃圾回收机制释放,最终导致内存耗尽,引起频繁的垃圾回收,从而影响CPU性能。
  • 过多的IO操作: 如果程序中涉及大量的IO操作,并且这些IO操作被频繁调用而且没有得到有效的处理,可能导致CPU占用率上升。
  • 大数据量的计算: 大规模的数据处理或者复杂的计算也可能导致CPU占用率飙升。

写一段代码模拟大量创建对象,并且频繁垃圾回收。

@GetMapping("/demo5")
public void demo5() {while (true) {allocateMemory();triggerGarbageCollection();}
}private static void allocateMemory() {// 模拟内存分配for (int i = 0; i < 100000; i++) {Object obj = new Object();}
}private static void triggerGarbageCollection() {// 模拟垃圾回收System.gc();
}

首先top命令查看cpu占用率,发现占用率高达98.3%
1705327619957.png
接着jstat -gcutil 3253 1000查看垃圾回收信息。FGC表示FullGC回收次数,FGCT表示FullGC回收上花费的总时间。
可以看出程序一直在频繁的进行FullGC,而YGC基本不变,几乎可以确定是FullGC导致CPU占用100%。
1705327902039.png
输入命令top Hp 3253找出进程下哪个线程占用cpu高

1705415838252.png
发现是一个VM Thread一直占用着资源,VM Thread 在执行与垃圾收集相关的底层任务时,可能导致 CPU 使用率升高。

3、线程死锁

先写一段模拟线程死锁的demo:

private static Object resource1 = new Object();
private static Object resource2 = new Object();@GetMapping("/demo4")
public void demo4(){Thread thread1 = new Thread(() -> {synchronized (resource1) {System.out.println("Thread 1: Holding lock 1...");try {Thread.sleep(1000); // Introducing delay to make deadlock more likely} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 1: Waiting for lock 2...");synchronized (resource2) {System.out.println("Thread 1: Holding lock 1 and lock 2...");}}});Thread thread2 = new Thread(() -> {synchronized (resource2) {System.out.println("Thread 2: Holding lock 2...");try {Thread.sleep(1000); // Introducing delay to make deadlock more likely} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 2: Waiting for lock 1...");synchronized (resource1) {System.out.println("Thread 2: Holding lock 2 and lock 1...");}}});thread1.start();thread2.start();
}

执行后控制台会输出线程互相等待的信息:
image.png

输入jstack -l 9085输出线程状态信息

Found one Java-level deadlock:
=============================
"Thread-1":waiting to lock monitor 0x00007f33cc2fc9b0 (object 0x00000000fb9ccec0, a java.lang.Object),which is held by "Thread-2""Thread-2":waiting to lock monitor 0x00007f33bc0d6c10 (object 0x00000000fb944b68, a java.lang.Object),which is held by "Thread-1"Java stack information for the threads listed above:
===================================================
"Thread-1":at com.bairimeng.keeplearning.demo.TestDemoController.lambda$demo4$3(TestDemoController.java:94)- waiting to lock <0x00000000fb9ccec0> (a java.lang.Object)- locked <0x00000000fb944b68> (a java.lang.Object)at com.bairimeng.keeplearning.demo.TestDemoController$$Lambda$756/0x0000000801050748.run(Unknown Source)at java.lang.Thread.run(java.base@17.0.6/Thread.java:833)
"Thread-2":at com.bairimeng.keeplearning.demo.TestDemoController.lambda$demo4$4(TestDemoController.java:111)- waiting to lock <0x00000000fb944b68> (a java.lang.Object)- locked <0x00000000fb9ccec0> (a java.lang.Object)at com.bairimeng.keeplearning.demo.TestDemoController$$Lambda$757/0x0000000801050968.run(Unknown Source)at java.lang.Thread.run(java.base@17.0.6/Thread.java:833)Found 1 deadlock.

从上面日志看出线程1内存地址为0x00000000fb9ccec0,线程2内存地址为0x00000000fb944b68
线程1 locked <0x00000000fb944b68>,表示当前已经被线程2加锁了,需要等待线程2释放锁。
线程2 locked <0x00000000fb9ccec0>,表示当前已经被线程1加锁了,需要等待线程1释放锁。
两者相互等待,导致了死锁,并且可以在日志中,找到第94和111行锁代码位置。

附加: 假如安装了阿里的arthas工具,更加方便进行分析。
启动后输入thread,看到两个线程处于BLOCKED阻塞状态

image.png
这样还不能清楚知道是否造成死锁,以及死锁的位置。接着输入thread -b

image.png
这次就能直接定位到死锁是在代码94行,并且从红色部分<---- but blocks 1 other threads!看出阻塞的线程数量。

结束

至此,jdk分析工具和jvm问题分析已经介绍完毕,点个赞再走啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/632896.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序进阶学习(视频完结)(核心,重点)

首先上面是一个视频播放器 把视频的宽度设置为100%即可铺满全屏 然后视频的标题和作者 最后就是一个视频播放列表 &#xff0c;设置一个固定位置开始滚动即可 还有一个问题没有解决&#xff0c;大家出出主意。 在播放页面在点击一个新的视频去播放&#xff0c;点进去的新视频获…

C/C++ BM6判断链表中是否有环

文章目录 前言题目解决方案一1.1 思路阐述1.2 源码 解决方案二2.1 思路阐述2.2 源码 总结 前言 做了一堆单链表单指针的题目&#xff0c;这次是个双指针题&#xff0c;这里双指针的作用非常明显。 题目 判断给定的链表中是否有环。如果有环则返回true&#xff0c;否则返回fal…

计算机网络-ACL实验

一、NAT实验配置 NAT实验配置 通过基本ACL匹配VLAN 10网段&#xff0c;然后在出口设备NAT转换只要匹配到VLAN10地址则进行转换。 核心交换机 # 配置VLAN和默认路由&#xff0c;配置Trunk和Access接口 interface Vlanif10ip address 192.168.10.254 255.255.255.0 # interface V…

深入了解WPF控件:基础属性与用法(五)

掌握WPF控件&#xff1a;熟练常用属性&#xff08;五&#xff09; Image 是一种在WPF应用程序中显示图片的方式。它可以用于显示静态图片&#xff0c;也可以用于显示动态图片&#xff0c;如GIF。此外&#xff0c;Image控件还可以自适应大小&#xff0c;根据容器的大小自动调整…

关于gltf模型格式文件的学习

目录 glTF模型 小黄鸭的gltf模型 字段分析 scene nodes meshes primitives attributes indices mode material accessors bufferView byteOffset count componentType type materials textures images samplers magFilter与minFilter wrapS与wrapT 进行…

高效火情监测,科技助力森林防火【数字地球开放平台】

数字地球开放平台-以卫星遥感为核心的空天信息服务开放平台 (geovisearth.com) 2019年3月30日&#xff0c;四川省凉山州木里县爆发了一场森林火灾&#xff0c;火点位于海拔3800米左右&#xff0c;地形险峻、坡度陡峭、谷深难以抵挡火势。在扑救的过程中&#xff0c;27名森林消防…

最小公倍数之和(莫比乌斯反演P3911)

路径&#xff1a; P3911 最小公倍数之和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路&#xff1a; 代码&#xff1a; #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<string> #include<cstring> #include<cmath> #include<…

JavaSE 万字总结知识点(期末复习指南)

目录 一.Java的特性 二.注释 三.数据类型 基本数据类型 包装类 引用数据类型 四.运算符 五.逻辑控制 选择语句 循环语句 六.数组 七.方法 八.类与对象 构造方法 内部类 九.继承和多态 十.抽象类与接口 抽象类 接口 十一.异常 一.Java的特性 Java最初由Sun…

助力焊接场景下自动化缺陷检测识别,基于YOLOv5【n/s/m/l/x】全系列参数模型开发构建工业焊接场景下缺陷检测识别分析系统

焊接是一个不陌生但是对于开发来说相对小众的场景&#xff0c;在我们前面的博文开发实践中也有一些相关的实践&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《轻量级模型YOLOv5-Lite基于自己的数据集【焊接质量检测】从零构建模型超详细教程》 《基于DeepLabV3Pl…

【华为 ICT HCIA eNSP 习题汇总】——题目集3

1、&#xff08;多选&#xff09;IEEE 802.11n支持在哪些频率下工作&#xff1f; A、2.5GHz B、2.4GHz C、5GHz D、6GHz 考点&#xff1a;无线局域网 解析&#xff1a;&#xff08;BC&#xff09; IEEE 820.11n 支持双频段&#xff0c;它兼容IEEE 802.11a 与IEEE 820.11b 两种标…

springcloud Ribbon负载均衡服务调用

文章目录 代码下载地址简介测试 Ribbon负载均衡算法手写RoundRobinRule源码8001/8002微服务改造80订单微服务改造测试 代码下载地址 地址:https://github.com/13thm/study_springcloud/tree/main/days6_Ribbon 简介 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端…

高级分布式系统-第15讲 分布式机器学习--神经网络理论

高级分布式系统汇总&#xff1a;高级分布式系统目录汇总-CSDN博客 神经网络理论 模糊控制在处理数值数据、自学习能力等方面还远没有达到人脑的境界。人工神经网络从另一个角度出发&#xff0c;即从人脑的生理学和心理学着手&#xff0c;通过人工模拟人脑的工作机理来实现机器…

2024年甘肃省职业院校技能大赛信息安全管理与评估 样题三 理论题

竞赛需要完成三个阶段的任务&#xff0c;分别完成三个模块&#xff0c;总分共计 1000分。三个模块内容和分值分别是&#xff1a; 1.第一阶段&#xff1a;模块一 网络平台搭建与设备安全防护&#xff08;180 分钟&#xff0c;300 分&#xff09;。 2.第二阶段&#xff1a;模块二…

学习Spring的第五天(Bean的依赖注入)

Bean的依赖注入有两种方式: 一 . 常规Bean的依赖注入 很简单,不过多赘述了,注意ref: 是构造函数或set方法的参数,一般为对象, value: 是构造函数或set方法的参数,一般为值. 看下图 1.1 下面来演示一下集合数据类型的关于Bean的依赖注入 1.1.1这是List的注入(演示泛型为Strin…

[NSSRound#16 Basic]RCE但是没有完全RCE

题目代码&#xff1a; <?php error_reporting(0); highlight_file(__file__); include(level2.php); if (isset($_GET[md5_1]) && isset($_GET[md5_2])) {if ((string)$_GET[md5_1] ! (string)$_GET[md5_2] && md5($_GET[md5_1]) md5($_GET[md5_2])) {i…

[python]裁剪文件夹中所有pdf文档并按名称保存到指定的文件夹

最近在写论文的实验部分&#xff0c;由于latex需要pdf格式的文档&#xff0c;审稿专家需要对pdf图片进行裁剪放大&#xff0c;以保证图片质量。 原图&#xff1a; 裁剪后的图像&#xff1a; 代码粘贴如下。将input_folder和output_folder替换即可。(x1, y1)&#xff0c; (x2…

小封装高稳定性振荡器 Sg2520egn / sg2520vgn, sg2520ehn / sg2520vhn

描述 随着物联网和ADAS等5G应用的实施&#xff0c;数据流量不断增长&#xff0c;网络基础设施变得比以往任何时候都更加重要。IT供应商一直在快速建设数据中心&#xff0c;并且对安装在数据中心内部/内部的光模块有很大的需求。此应用需要具有“小”&#xff0c;“低抖动”和“…

Flutter中使用minio_new库

前言 在移动开发中&#xff0c;我们常常会遇到需要在App中处理文件上传和下载的需求。Minio是一个开源的对象存储服务&#xff0c;它兼容Amazon S3云存储服务接口&#xff0c;可以用于存储大规模非结构化的数据。 开始之前 在pubspec.yaml文件中添加minio_new库的依赖&#xf…

CSV文件中json列的处理2

如上所示&#xff0c;csv文件中包含以中括号{}包含的json字段&#xff0c;可用如下方法提取&#xff1a; import pandas as pd from datetime import date todaystr(date.today()) import jsonfilepath/Users/kangyongqing/Documents/kangyq/202401/调课功能使用统计/ file104…

kotlin Kmp多平台模板生成

地址: Kotlin Multiplatform Wizard | JetBrains 可生成kotlin多个平台模板 https://terrakok.github.io/Compose-Multiplatform-Wizard/