四.从零开始JVM实战高手-JVM监控及线上问题定位

news2025/1/29 13:59:46

前言

Java程序员面试JVM几乎比问,对于JVM监控,线上OOM,CPU负载100%等问题也是经常被问到,尽管在企业中不一定轮得到我们去处理线上问题,但是不管是为了面试还是为了应对开发那么对于JVM线上问题处理都是必须要去了解的。

相对而言,解决故障问题也好,处理性能瓶颈也罢,通常思路大致都是相同的,即:分析数据(日志) , 分析排查,问题定位,解决问题 ,如果我们连程序执行的数据或日志都拿不到,那么我们是没办法去定位问题的。

庆幸的是Java提送了JVM监视工具以及相关指令来帮助我们获取JVM相关数据来帮助我们进行问题排查。

工具安装

我们只知道有JVM的存在,但它的运行对于我们来说感觉像是摸不着看不见的,所以我们需要借助工具来监控它的一个实时状态,就像Windows的性能监视器一样,JDK也有自己的可视化工具.Java提供了2个监视工具:

  • D:\opensource\jdk1.8\bin\jconsole.exe

  • D:\opensource\jdk1.8\bin\jvisualvm.exe

jconsole

通过cmd命令行输入 jconsole 弹出如下界面
在这里插入图片描述
选择java进行后进入,进入后可以看到内存的情况,类加载情况,线程情况等等。
在这里插入图片描述

Jvisualvm

我们以运行cmd ,输入jvisualvm,将Java VisualVM启动
在这里插入图片描述
左边本地菜单下是java进程,选择一个进程后右边可以看到堆,类加载情况,现成情况等

jvisualvm安装GC插件

自带的jvisualvm没有监视GC垃圾回收功能,我们需要额外安装插件:

打开工具 -> 插件 -> 选择“可用插件”页 : 我们在这里安装一个Visual GC,方便我们看到内存回收以及各个分代的情况 . 打上勾之后点击安装,就是常规的next以及同意协议等 ,网络不是很稳定,有时候可能需要多尝试几次。可以在设置中修改插件中心地址:

在这里插入图片描述

根据如下步骤修改地址:找到插件中心

http://visualvm.github.io/pluginscenters.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EY48yUBo-1683959030847)(课件图片/wps3517.tmp.jpg)]

找到对应的JDK版本:

http://visualvm.github.io/archive/uc/8u40/updates.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzJYYDpg-1683959030848)(课件图片/1638788437854.png)]
复制插件地址:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0IxXMl7y-1683959030849)(课件图片/1638788450829.png)]

安装插件:

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

然后再 可用插件中 找到 Visual GC

安装完成后我们将当前监控页关掉,再次打开,就可以看到Profiler后面多了一个Visual GC页。

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

在这里我们可以看到JIT活动时间,类加载活动时间,GC活动时间以及各个分代的情况。

需要注意的是,当前课件使用的JDK版本为1.8,仍然自带了VisualVM,从1.9开始的版本是没有自带的,需要额外下载,下载的github地址:

https://visualvm.github.io/download.html

另外,如果开发工具使用的是Intellij IDEA的话,可以下载一个插件,VisualVM Launcher,通过插件启动可以直接到上述页面,不用在左边的条目中寻找自己的项目.

当然也有其他的工具,但这个在可预见的未来都会是主力发展的多合一故障处理工具.所以我们后面将会使用这个工具来分析我们的JVM运行情况,进而优化.而需要优化我们还需要对JVM的组成有进一步的了解

JVM监控命令

在生产环境中,经常会遇到各种各样奇葩的性能问题,我们可以通过Java提供的JVM监控命令来达到监控和查看效果,相关命令如下

名称主要作用
jps查看正在运行的Java进程
jstack打印线程快照
jmap导出堆内存映像文件
jstat查看jvm统计信息
jinfo实时查看和修改jvm配置参数
jhat用于分析heapdump文件

jps 查看进程

jps可以列出正在运行的Java进程,并显示虚拟机执行主类(Main Class,main()函数所在的类)名称以及进程id ,通过 jps -help 可以查看参数

选项作用
-q只输出进程id
-m输出传递给主类main函数的参数
-l输出主类全类名,如果进程执行的是Jar包,输出jar包名字
-v程序启动时指定的jvm参数

案例演示:
在这里插入图片描述

jstack:打印线程快照

一般在生产环境中发生了长时间停顿,卡死,死锁,请求时间长等问题就可以通过打印线程快照来分析定位问题. 下面是一段死锁的代码:

public class DeadlockExample {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();
     public static void main(String[] args) {
        Thread t1 = new Thread() {
            public void run() {
                synchronized (lock1) {
                    System.out.println("Thread 1 acquired lock 1");
                    try {
                        Thread.sleep(1000);
                        synchronized (lock2) {
                            System.out.println("Thread 1 acquired lock 2");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
         Thread t2 = new Thread() {
            public void run() {
                synchronized (lock2) {
                    System.out.println("Thread 2 acquired lock 2");
                    try {
                        Thread.sleep(1000);
                        synchronized (lock1) {
                            System.out.println("Thread 2 acquired lock 1");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
         t1.start();
         t2.start();
         try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

然后我们先试用 jps -l 查看进程
在这里插入图片描述
拿到进程id,使用jstack查看每个线程快照 : jstack 27928
在这里插入图片描述
从线程快照中可以看到当前进程中的所有线程。其中包括我们的代码的线程,状态是 locked 锁住状态。同时它提示 found 1 deadlock 发现死锁,并给出了死锁出现的位置。

jmap:导出堆快照

执行jmap -histo pid可以打印出当前堆中所有每个类的实例数量和内存占用,如下,class name是每个类的类名([B是byte类型,[C是char类型,[I是int类型),bytes是这个类的所有示例占用内存大小,instances是这个类的实例数量:
在这里插入图片描述
执行jmap -dump 可以转储堆内存快照到指定文件,比如执行

jmap -dump:format=b,file=/data/jvm/dumpfile_jmap.hprof PID ,可以把当前堆内存的快照转储到dumpfile_jmap.hprof文件中,然后可以对内存快照进行分析。

生产环境我们一般会配置,让虚拟机在OOM异常出现之后自动生成dump文件

-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=/Users

比如现在有一个死循环的代码然后运行到一定时间会导致内存溢出

public class Main {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        int i = 0;
        while(true){
            arrayList.add(new Main());
            System.out.println(i++);
        }
    }
}

为了效果明显,我们把堆设置小一些,拉后把HeapDumpOnOutOfMemoryError设置上

-Xms2m
-Xmx2m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=d:\

给当前应用设置上以上VM参数,执行代码演示效果如下
在这里插入图片描述
然后我们找到hprof 堆快照文件,可以通过 jvisualvm工具去装载分析
在这里插入图片描述
现在快照文件,文件类型需要选择堆快照
在这里插入图片描述
装载后可以直接看到内存溢出的错误信息
在这里插入图片描述
点击线程名字 main 进去后可以直接定位到内存溢出的位置
在这里插入图片描述

jstat:监视虚拟机信息

jstat -gc pid 500 10 :pid是线程ID,每500毫秒打印一次Java堆状况(各个区的容量、使用容量、gc时间等信息),打印10次
在这里插入图片描述
jstat还可以以其他角度监视各区内存大小、监视类装载信息等,具体可以google jstat的详细用法。下面是结果对照

S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
单位:KB

jinfo : 查看进程参数

jinfo(Configuration Info for Java) 查看虚拟机配置参数信思,也可用于调整虚拟机的配置参数。

在很多情况下,Java应用程序不会指定所有的Java虚拟机参数。而此时,开发人员可能不知道某一个具体的Java虚拟机参数的默认值。在这种情况下,可能需要通过查找文档获取某个参数的默认值。这个查找过程可能是非常艰难的。但有了 jinfo工具,开发人员可以很方便地找到Java虚拟机参数的当前值。

jinfo不仅可以查看运行时某一个Java虚拟机参数的实际取值, 甚至可以在运行时修改部分参 数,并使之立即生效。 但是,并非所有参数都支持动态修改。参数只有被标记 manageable的flag可以被实时修改。其实,这个修改能力是 极其有限的。
在这里插入图片描述

VM Flags:
Non-default VM flags: -XX:CICompilerCount=12 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=null -XX:InitialHeapSize=2097152 -XX:MaxHeapSize=209715200 -XX:MaxNewSize=69730304 -XX:MinHeapDeltaBytes=524288 -XX:OldSize=524288 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Command line: -Xms2m -Xmx200m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:\ -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\lib\idea_rt.jar=54817:D:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\bin -Dfile.encoding=UTF-8

通过 jinfo -flags pid: 查看曾经赋过值的参数值

在这里插入图片描述
jinfo不仅可以查看运行时某一个Java虚拟机参数的实际取值, 甚至可以在运行时修改部分参 数,并使之立即生效。 但是,并非所有参数都支持动态修改。参数只有被标记 manageable的flag可以被实时修改。可以通过使用java -XX:+PrintFlagsInitial | grep manageable命令来查看manageable
在这里插入图片描述
修改格式如下:

  • 对于布尔类型修改 : jinfo -flag ±参数 pid
  • 对于非布尔类型: jinfo -flag 参数名=参数值 pid

比如修改打印GC日志演示如下 : jinfo -flag +PrintGCDetails PID

//查看进程
C:\Users\Administrator>jps -l
20924 org.example.Main
...

//查看是否设置PrintGCDetails参数配置
C:\Users\Administrator>jinfo -flag PrintGCDetails 20924

//增加jvm参数:打印GC详情
C:\Users\Administrator>jinfo -flag +PrintGCDetails 20924

//查看是否设置PrintGCDetails参数配置
C:\Users\Administrator>jinfo -flag PrintGCDetails 20924
-XX:+PrintGCDetails
...

解决线上CPU100%问题

一般 CPU 100%,基本都是代码死循环造成的。排查的核心思路是 找到对应服务器,定位是哪个进程的哪个线程中的哪些代码引发的问题,可以简单介绍当时的异常代码示例。

第一步找到最耗CPU的进程 : 使用top -c 查看进程,然后输入大 P,按照 CPU 的使用率进行排序。
在这里插入图片描述
第二步找到进程中最耗CPU的线程 :找到CPU最高的进程,找到进程ID (PID) , 通过命令 top -Hp PID 找到这个进程对应的线程,然后输入大 P ,按照 CPU 的使用率进行排序。
在这里插入图片描述
拿到排第一的PID就是最耗时的线程ID,然后使用 printf “%x\n” PID 把PID由十进制转换为十六进制(之所以要转化为16进制,是因为堆栈里,线程id是用16进制表示的。)

[root@VM-4-2-centos ~]# printf "%x\n" 13759
35bf

接着,我们需要使用 jstack 打印进程的堆栈信息,再通过 grep 查看对应线程相关的东西。jstack 进程ID | grep “线程ID” -C5 --color

jstack 30979  | grep "35bf" -C5 --color

这个时候就可以打印出代码,然后从打印的线程快照中匹配nid,就可以定位到哪个线程耗时了,同时可以快速定位到代码,可以看到是哪个类中的哪个方法导致此次 CPU 100% 的原因了。
在这里插入图片描述

远程监控

如果你不是很熟悉命令,使用命令监控JVM是一件痛苦的事情,JVisualvm提供了jmx远程功能。默认是通过localhost的ip地址提供RMI服务,它需要我们在远程配置JVM参数来开启远程连接

-Xms256m 
-Xmx512m 
-Xss256m 
-XX:PermSize=512m 
-XX:MaxPermSize=1024m 

-Dcom.sun.management.jmxremote 
-Djava.rmi.server.hostname=服务器IP 
#远程服务的端口:
-Dcom.sun.management.jmxremote.port=9015 
#客户端 rmi通信端口
-Dcom.sun.management.jmxremote.rmi.port=9015 
#关闭ssl功能
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.authenticate=false 

然后我们在本地Jvisualvm添加远程主机
在这里插入图片描述
然后添加jmx远程连接
在这里插入图片描述
制定远程连接参数,取消ssl连接ss
文章结束,如果对你有帮助请给个好评,你的鼓励是我最大的动力

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

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

相关文章

二、软件工程——Modeling

1.Undestanding Requirements System Engineering(系统工程) !!!Requirements Engieering(需求建模) 1.Inception(开始):(确认利益相关者) 2.…

164道网络安全工程师面试题(附答案)

为了更好地帮助大家高薪就业,今天就给大家分享两份网络安全工程师面试题,一共有164道面试真题,希望它们能够帮助大家在面试中,少走一些弯路、更快拿到offer! 93道网络安全面试题 1、什么是SQL注入攻击 2、什么是XSS…

Linux中的特殊进程:idle进程(0号进程)、init进程(1号进程,被systemd 取代 )、kthreadd进程(2号进程)

文章目录 1. Linux中的三个特殊进程:2. idle进程、init进程、kthreadd进程的创建3 kthreadd进程3.1 kthreadd进程的启动3.2 kthreadd进程创建子进程3.3 kthreadd的工作流程总结 4. init进程4.1 init进程的启动 4. 总结参考 1. Linux中的三个特殊进程: i…

【作者踩坑总结0错版】vscode配置codeql-MacBook(M1/M2芯片-arm)

文章目录 前言一、codeql是什么二、安装必备工具2.1、安装codeql-cli2.2、安装codeql仓库2.3、桌面新建文件夹2.4、安装vscode 三、配置codeql环境变量四、vscode配置codeql插件4.1、下载测试靶场4.2、生成数据库文件(最难最多bug)4.3、vscode配置ql数据…

[入门必看]数据结构5.3:二叉树的遍历和线索二叉树

[入门必看]数据结构5.3:二叉树的遍历和线索二叉树 第五章 树与二叉树5.3 二叉树的遍历和线索二叉树知识总览5.3.1_1 二叉树的先中后序遍历5.3.1_2 二叉树的层次遍历5.3.1_3 由遍历序列构造二叉树5.3.2_1 线索二叉树的概念5.3.2_2 二叉树的线索化5.3.2_3 在线索二叉树…

服务(第十九篇)mysql的备份和恢复

备份的类型: ①物理备份:备份数据库的物理文件,数据文件和日志文件; 冷备份:关闭数据库进行的; 热备份:数据库处于运行中状态,依赖于日志文件; 温备份:数…

Cisco NX-OS Software Release 10.3(3)F - 网络操作系统软件

Cisco NX-OS Software Release 10.3(3)F - 网络操作系统软件 请访问原文链接:https://sysin.org/blog/cisco-nx-os-10/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org Cisco NX-OS Cisco NX-OS 操作系统助力网络紧…

Golang中map基础

目录 map类型 声明和初始化 元素操作 大小和容量 并发问题 实现原理 注意 map类型 Go语言中的map类型是一种key-value(键值对)结构,用于存储一组无序的数据,其中键是唯一的,值可以相同或者不同。在Go语言的程序…

button 设置外观

导入图片 设置拉伸 设置按钮的FlatStyle属性为Flat 设置透明背景

先到先得先学,2023最新国内大厂最新Java面试八股文总结

“铜三铁四”接近尾声,身边被迫“毕业”或者主动在这个时间点跳槽的大佬基本该找到工作的都找到工作了,找不到的也大多数都已经躺平了(手动狗头),只剩一群“45度人”(卷不动,躺不平的人&#xf…

用于跨中心多边形分割的任务相关特征补充

文章目录 Task-Relevant Feature Replenishment for Cross-Centre Polyp Segmentation摘要本文方法Domain-Invariant Feature Decomposition (DIFD)Task-Relevant Feature ReplenishmentPolyp-Aware Adversarial Learning (PAAL)总损失函数 结果 Task-Relevant Feature Repleni…

AI翻译工具分享

LanguageX简介 冠军翻译引擎交互式翻译,为专业译者而生的新一代译者工具 自2017年神经网络机器翻译流行以来,“机器翻译替代人工翻译”的观点屡见不鲜。作为一个技术和语言背景结合的团队,LanguageX认为语言服务是高度复杂、需要创意的智力活动,并肩负着人类沟通的重要使命。La…

机器学习之k-means聚类二、啤酒聚类实现

文章目录 一、环境开发说明二、基于前篇理论实现1. 数据说明2. 具体实现流程a. 对数据进行标准化处理b. 使用手肘法进行K值得选择c. 算法实现,d.将聚类结果进行可视化e. 计算轮廓系数f. 轮廓系数可视化 三、 文中的完整代码四、不调包实现五、参考文献 一、环境开发…

MySQL-图形化界面工具 (下

♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️树高千尺,落叶归根人生不易&…

ip route 和 route 命令

ip route 属于 iproute2 工具包;route 属于 net-tools 工具包;目前 ip route 使用较为广泛; 两个工具包的介绍请见 “iproute2 和 net-tools 介绍” 文章。 ip route命令介绍 ip route命令用于管理静态路由表。 linux 系统中,可…

【GigaGAN论文精读】Scaling up GANs for Text-to-Image Synthesis

【GigaGAN论文精读】Scaling up GANs for Text-to-Image Synthesis 0、前言Abstract1. Introduction(图放在文末)2. Related Works2.1Text-to-image synthesis.2.2GAN-based image synthesis.2.3Super-resolution for large-scale text-to-image models.…

【Java|基础篇】面向对象三大特性之封装

文章目录 1.前言2.什么是封装3.如何更好的封装4.封装的好处5.总结 1.前言 面向对象有三个特性:封装,继承和多态,本文主要讲解封装. 封装是面向对象编程的基本原则之一,它可以提高程序的安全性、稳定性、可维护性和可扩展性,减少了代码的冗余&#xff0c…

【框架源码】Spring源码核心注解Conditional和应用

1.什么是Conditional注解 Conditional来源于spring-context包下的一个注解。通过Conditional配置一些条件判断,当所有条件都满足时,被该Conditional注解标注的目标才会被Spring处理。 例如根据当前环境、系统属性、配置文件等条件来决定是否注册某个Bea…

股票量价关系基础知识3

成交量变化的表现形式 成交量变化的表现形式主要有以下几种 一、缩量 一般界定当日成交量低于前一交易日成交量10%以上为缩量。 缩量往往发生在上升途中的回调末期、上涨末期以及股价下跌的中后期。一般下跌趋势中的缩量阶段,往往意味着股价仍未见底,不能…

服务攻防-中间件安全CVE复现IISApacheTomcatNginx漏洞复现

目录 一、导图 二、ISS漏洞 中间件介绍> 1、短文件 2、文件解析 3、HTTP.SYS 4、cve-2017-7269 三、Nignx漏洞 中间件介绍> 1、后缀解析漏洞 2、cve-2013-4547 3、cve-2021-23017 无 EXP 4、cve-2017-7529 意义不大 四、Apache漏洞 中间件介绍> 1、漏…