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问题分析已经介绍完毕,点个赞再走啦!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

记录自己 自学摸索 学习日语:新标日

新标日 几节课合成在一起太乱了 第四课会更新个集合 自学日语吧算是 摸索中&#xff01; 尽量一周一课 第一课 单词 ちゅうごくじん 中国人 中国人 にほんじん 日本人 日本人 あめりかじん アメリカ人 美国人 かんこくじん 韓国人 韩国人 ふらんすじん フラン…

基于FPGA的图像双边滤波实现,包括tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 双边滤波数学模型 4.2 双边滤波的特性 4.3 FPGA实现架构 5.算法完整程序工程 1.算法运行效果图预览 将FPGA数据导入到matlab对比测试&#xff1a; 2.算法运行软件版本 vivado2019.2 …

极致画质与流畅播放的完美结合,只在ProVideoPlayer for Mac!

ProVideoPlayer for Mac 是一款功能强大的专业级视频播放软件&#xff0c;旨在提供出色的用户体验和无与伦比的功能。以下是它的一些主要功能介绍&#xff1a; 多格式兼容&#xff1a;ProVideoPlayer for Mac 支持广泛的视频格式&#xff0c;包括常见的MP4、AVI、MOV&#xff0…

docker里Java服务执行ping命令模拟流式输出

文章目录 业务场景处理解决实现ping功能并实时返回输出实现长ping和中断请求docker容器找不到ping命令处理 业务场景 我们某市的客户&#xff0c;一直使用CS版本的信控平台&#xff0c;直接安装客户Windows server服务器上&#xff0c;主要对信号机设备进行在线管理、方案配时…

输电线路分布式故障定位监测装置:保障电力系统的稳定运行

随着电力系统的不断发展和电力需求的日益增长&#xff0c;输电线路的稳定性和安全性显得尤为重要。为了确保电力系统的正常运行&#xff0c;我国科研人员研发出了一种新型的输电线路分布式故障定位监测装置&#xff0c;它能够实时监测输电线路的运行状态&#xff0c;及时发现并…

全网最全Stable diffusion保姆级教程「安装-配置-画图」,小白必收藏!!

随着chat gpt爆火之后&#xff0c;越来越多的人开始关注人工智能&#xff0c;人工智能相关的其他应用如AI绘画&#xff0c;也再次得到人们的关注。AI绘画的确很上头&#xff0c;最近几天小编也研究一下&#xff0c;这里把研究的过程以及中间遇到的问题整理一下&#xff0c;我这…

【办公类-21-02】20240118育婴员操作题word打印2.0

作品展示 把12页一套的操作题批量制作10份&#xff0c;便于打印 背景需求 将昨天整理的育婴师操作题共享&#xff0c; 因为题目里面有大量的红蓝颜色文字&#xff0c;中大班办公室都是黑白单面手动翻页打印。只有我待的教务室办公室有彩色打印机打印&#xff08;可以自动双面…

每日一题——LeetCode1252.奇数值单元格的数目

进阶&#xff1a;你可以设计一个时间复杂度为 O(n m indices.length) 且仅用 O(n m) 额外空间的算法来解决此问题吗&#xff1f; 方法一 直接模拟&#xff1a; 创建一个n x m的矩阵&#xff0c;初始化所有元素为0&#xff0c;对于indices中的每一对[ri,ci]&#xff0c;将矩…

精品量化公式——“低位擒妖”,擒妖指标终极版,成功率百分99,无未来

不多说&#xff0c;直接上效果如图&#xff1a; ► 日线表现 代码评估 技术指标代码评估&#xff1a; X_1:55; X_2:34; 这两行设定了两个变量 X_1 和 X_2 的值分别为 55 和 34。这些值可能用于后续计算的时间周期参数。 X_3:100*(HHV(HIGH,X_1)-CLOSE)/(HHV(HIGH,X_1)-LLV(…

鹅厂有料有趣的程序员交流圈重磅官宣!加入立享福利

号外&#xff01;腾讯云开发者社区重磅上线海量社群&#xff0c;覆盖开发者技术学习交流、工作成长、生活分享等多元场景需求&#xff0c;用最新鲜的内容&#xff0c;最好玩的互动&#xff0c;与你一起共创最有料有趣的技术人交流圈&#xff5e; 最有料有趣交流圈在这里你可以畅…

oracle中常用的一些函数,巧用函数,减少代码量

1.sign(x) 【功能】参数x为数值型或者字符串数值型&#xff0c;若为正值返回1,负值回-1&#xff0c;0返回0 2.floor(x) 【功能】返回小于等于x的最大整数值 3.ceil(x) 【功能】返回大于等于x的最小整数值 4.round(x[,y]) 【功能】返回四舍五入后的值 参数x,y数字型表达式,…

微信小程序(四)页面跳转

注释很详细&#xff0c;直接上代码 上一篇 新增内容 1.相对路径页面跳转 2. 绝对路径页面跳转 index.wxml <!-- navigator是块级元素&#xff0c;占一整行 --> <!-- 页面跳转url&#xff0c;相对路径 --> <navigator url"../logs/logs"><butto…

MySQL主从复制原理与实践:从配置到故障监控

文章目录 前言主从复制原理复制源主节点的工作从节点的工作复制流程的设计 主从复制环境搭建一、主从节点配置二、从节点开启复制步骤1、备份主节点的数据2、将数据同步到从节点3、从节点复制参数配置 三、验证复制环境 主从复制故障监控监控主从复制状态监控主从复制延迟 总结…

Improving Generative Modelling in VAEs Using Multimodal Prior

local representation vector r&#xff0c; ϵ \epsilon ϵ is i.i.d Gaussian 额外信息 作者未提供代码

【机组】时序与启停实验的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 &#x1f33a;一、 实验目…

ABAP IDOC 相关报表

上代码 干货来了 *&---------------------------------------------------------------------* *& REPORT ZRPT_FI_IDOC_R *& *&---------------------------------------------------------------------* *& author ABAP02 *& date 20240103 *&…

K8S-YAML

一、Kubernetes对象的描述 kubernetes中资源可以使用YAML描述&#xff08;如果您对YAML格式不了解&#xff0c;可以参考YAML语法&#xff09;&#xff0c;也可以使用JSON。其内容可以分为如下四个部分&#xff1a; typeMeta&#xff1a;对象类型的元信息&#xff0c;声明对象…

共同学习|Spring Cloud Alibaba一一Nacos介绍

接着上篇我们介绍的Spring Cloud Alibaba&#xff0c;下面来继续学习构建云原生应用的动态服务发现、配置管理和服务管理平台——Nacos介绍。 共同学习|Spring Cloud Alibaba一一简介篇-CSDN博客 3、Nacos介绍 Redirecting to: https://nacos.io/ 什么是nacos&#xff1f; …

高级RAG(八): 自动合并检索(Auto-merging Retrieval)

自动合并检索(Auto-merging Retrieval)是LlamaIndex的另外一种高级RAG技术&#xff0c;它有点类似与我们之间介绍的从小到大的检索&#xff0c;不过自动合并检索要比“从小到大的检索”稍微复杂一些&#xff0c;它首先将文档按一定的层次结构进行切割&#xff0c;然后在检索的时…

Docker 安装 MongoDb4

Docker 安装mongoDb 获取mongodb安装问题汇总参考 获取mongodb 注意&#xff1a; WARNING: MongoDB 5.0 requires a CPU with AVX support, and your current system does not appear to have that! **hub官网&#xff08;需要梯子&#xff09;&#xff1a;**https://hub.dock…