【Java开发日记】6个Java 工具,轻松分析定位 JVM 问题 !

目录

使用 JDK 自带工具查看 JVM 情况

jps

jinfo

jvisualvm

jcm


使用 JDK 自带工具查看 JVM 情况

JDK 自带了很多命令行甚至是图形界面工具,帮助查看 JVM 的一些信息。比如,在机器上运行 ls 命令,可以看到 JDK 8 提供了非常多的工具或程序:
 

接下来介绍些常用的监控工具。也可以先通过下面这张图了解下各种工具的基本作用:

为了测试这些工具,先来写一段代码:启动 10 个死循环的线程,每个线程分配一个 10MB 左右的字符串,然后休眠 10 秒。可以想象到,这个程序会对 GC 造成压力:

//启动10个线程
IntStream.rangeClosed(1, 10).mapToObj(i -> new Thread(() -> {while (true) {//每一个线程都是一个死循环,休眠10秒,打印10M数据String payload = IntStream.rangeClosed(1, 10000000).mapToObj(__ -> "a").collect(Collectors.joining("")) + UUID.randomUUID().toString();try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(payload.length());}
})).forEach(Thread::start);
TimeUnit.HOURS.sleep(1);

修改 pom.xml,配置 spring-boot-maven-plugin 插件打包的 Java 程序的 main 方法类:

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>org.geekbang.time.commonmistakes.troubleshootingtools.jdktool.CommonMistakesApplication
</mainClass>
</configuration>
</plugin>

然后使用 java -jar 启动进程,设置 JVM 参数,让堆最小最大都是 1GB:

java -jar common-mistakes-0.0.1-SNAPSHOT.jar -Xms1g -Xmx1g

完成这些准备工作后,就可以使用 JDK 提供的工具,来观察分析这个测试程序了。 

jps

首先,使用 jps 得到 Java 进程列表,这会比使用 ps 来的方便:

➜  ~ jps
12707
22261 Launcher
23864 common-mistakes-0.0.1-SNAPSHOT.jar
15608 RemoteMavenServer36
23243 Main
23868 Jps
22893 KotlinCompileDaemon

jinfo

然后,可以使用 jinfo 打印 JVM 的各种参数:

➜  ~ jinfo 23864
Java System Properties:
#Wed Jan 29 12:49:47 CST 2020
...
user.name=zhuye
path.separator=\:
os.version=10.15.2
java.runtime.name=Java(TM) SE Runtime Environment
file.encoding=UTF-8
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
...
VM Flags:
-XX:CICompilerCount=4 -XX:ConcGCThreads=2 -XX:G1ConcRefinementThreads=8 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=268435456 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=4294967296 -XX:MaxNewSize=2576351232 -XX:MinHeapDeltaBytes=1048576 -XX:NonNMethodCodeHeapSize=5835340 -XX:NonProfiledCodeHeapSize=122911450 -XX:ProfiledCodeHeapSize=122911450 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
VM Arguments:
java_command: common-mistakes-0.0.1-SNAPSHOT.jar -Xms1g -Xmx1g
java_class_path (initial): common-mistakes-0.0.1-SNAPSHOT.jar
Launcher Type: SUN_STANDARD

查看第 15 行和 19 行可以发现,设置 JVM 参数的方式不对,**-Xms1g** 和 **-Xmx1g** 这两个参数被当成了 Java 程序的启动参数,整个 JVM 目前最大内存是 4GB 左右,而不是 1GB。
因此,当怀疑 JVM 的配置很不正常的时候,要第一时间使用工具来确认参数。除了使用工具确认 JVM 参数外,也可以打印 VM 参数和程序参数:

System.out.println("VM options");
System.out.println(ManagementFactory.getRuntimeMXBean().getInputArguments().stream().collect(Collectors.joining(System.lineSeparator())));
System.out.println("Program arguments");
System.out.println(Arrays.stream(args).collect(Collectors.joining(System.lineSeparator())));

把 JVM 参数放到 -jar 之前,重新启动程序,可以看到如下输出,从输出也可以确认这次 JVM 参数的配置正确了:

➜  target git:(master) ✗ java -Xms1g -Xmx1g -jar common-mistakes-0.0.1-SNAPSHOT.jar test
VM options
-Xms1g
-Xmx1g
Program arguments
test

jvisualvm

然后,启动另一个重量级工具 jvisualvm 观察一下程序,可以在概述面板再次确认 JVM 参数设置成功了:

继续观察监视面板可以看到,JVM 的 GC 活动基本是 10 秒发生一次,堆内存在 250MB 到 900MB 之间波动,活动线程数是 22。可以在监视面板看到 JVM 的基本情况,也可以直接在这里进行手动 GC 和堆 Dump 操作:

jconsole如果希望看到各个内存区的 GC 曲线图,可以使用 jconsole 观察。jconsole 也是一个综合性图形界面监控工具,比 jvisualvm 更方便的一点是,可以用曲线的形式监控各种数据,包括 MBean 中的属性值:

 jstat

同样,如果没有条件使用图形界面(毕竟在 Linux 服务器上,主要使用命令行工具),又希望看到 GC 趋势的话,可以使用 jstat 工具。
jstat 工具允许以固定的监控频次输出 JVM 的各种监控指标,比如使用 -gcutil 输出 GC 和内存占用汇总信息,每隔 5 秒输出一次,输出 100 次,可以看到 Young GC 比较频繁,而 Full GC 基本 10 秒一次:

➜  ~ jstat -gcutil 23940 5000 100S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT0.00 100.00   0.36  87.63  94.30  81.06    539   14.021    33    3.972   837    0.976   18.9680.00 100.00   0.60  69.51  94.30  81.06    540   14.029    33    3.972   839    0.978   18.9790.00   0.00   0.50  99.81  94.27  81.03    548   14.143    34    4.002   840    0.981   19.1260.00 100.00   0.59  70.47  94.27  81.03    549   14.177    34    4.002   844    0.985   19.1640.00 100.00   0.57  99.85  94.32  81.09    550   14.204    34    4.002   845    0.990   19.1960.00 100.00   0.65  77.69  94.32  81.09    559   14.469    36    4.198   847    0.993   19.6590.00 100.00   0.65  77.69  94.32  81.09    559   14.469    36    4.198   847    0.993   19.6590.00 100.00   0.70  35.54  94.32  81.09    567   14.763    37    4.378   853    1.001   20.1420.00 100.00   0.70  41.22  94.32  81.09    567   14.763    37    4.378   853    1.001   20.1420.00 100.00   1.89  96.76  94.32  81.09    574   14.943    38    4.487   859    1.007   20.4380.00 100.00   1.39  39.20  94.32  81.09    575   14.946    38    4.487   861    1.010   20.442

其中,S0 表示 Survivor0 区占用百分比,S1 表示 Survivor1 区占用百分比,E 表示 Eden 区占用百分比,O 表示老年代占用百分比,M 表示元数据区占用百分比,YGC 表示年轻代回收次数,YGCT 表示年轻代回收耗时,FGC 表示老年代回收次数,FGCT 表示老年代回收耗时。
jstat 命令的参数众多,包含 -class-compiler-gc 等。Java 8、Linux/Unix 平台 jstat 工具的完整介绍,可以查看这里。jstat 定时输出的特性,可以方便持续观察程序的各项指标。
继续来到线程面板可以看到,大量以 Thread 开头的线程基本都是有节奏的 10 秒运行一下,其他时间都在休眠,和代码逻辑匹配:

点击面板的线程 Dump 按钮,可以查看线程瞬时的线程栈:

 jstack

通过命令行工具 jstack,也可以实现抓取线程栈的操作:

➜  ~ jstack 23940
2020-01-29 13:08:15
Full thread dump Java HotSpot(TM) 64-Bit Server VM (11.0.3+12-LTS mixed mode):
...
"main" #1 prio=5 os_prio=31 cpu=440.66ms elapsed=574.86s tid=0x00007ffdd9800000 nid=0x2803 waiting on condition  [0x0000700003849000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(java.base@11.0.3/Native Method)
at java.lang.Thread.sleep(java.base@11.0.3/Thread.java:339)
at java.util.concurrent.TimeUnit.sleep(java.base@11.0.3/TimeUnit.java:446)
at org.geekbang.time.commonmistakes.troubleshootingtools.jdktool.CommonMistakesApplication.main(CommonMistakesApplication.java:41)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@11.0.3/Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@11.0.3/NativeMethodAccessorImpl.java:62)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@11.0.3/DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(java.base@11.0.3/Method.java:566)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:51)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52)
"Thread-1" #13 prio=5 os_prio=31 cpu=17851.77ms elapsed=574.41s tid=0x00007ffdda029000 nid=0x9803 waiting on condition  [0x000070000539d000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(java.base@11.0.3/Native Method)
at java.lang.Thread.sleep(java.base@11.0.3/Thread.java:339)
at java.util.concurrent.TimeUnit.sleep(java.base@11.0.3/TimeUnit.java:446)
at org.geekbang.time.commonmistakes.troubleshootingtools.jdktool.CommonMistakesApplication.lambda$null$1(CommonMistakesApplication.java:33)
at org.geekbang.time.commonmistakes.troubleshootingtools.jdktool.CommonMistakesApplication$$Lambda$41/0x00000008000a8c40.run(Unknown Source)
at java.lang.Thread.run(java.base@11.0.3/Thread.java:834)
...

抓取后可以使用类似fastthread(https://fastthread.io/)这样的在线分析工具来分析线程栈。 

jcmd

最后,来看一下 Java HotSpot 虚拟机的 NMT 功能。
通过 NMT,可以观察细粒度内存使用情况,设置 -XX:NativeMemoryTracking=summary/detail 可以开启 NMT 功能,开启后可以使用 jcmd 工具查看 NMT 数据。
重新启动一次程序,这次加上 JVM 参数以 detail 方式开启 NMT:

-Xms1g -Xmx1g -XX:ThreadStackSize=256k -XX:NativeMemoryTracking=detail

在这里,还增加了 -XX:ThreadStackSize 参数,并将其值设置为 256k,也就是期望把线程栈设置为 256KB。通过 NMT 观察一下设置是否成功。
启动程序后执行如下 jcmd 命令,以概要形式输出 NMT 结果。可以看到,当前有 32 个线程,线程栈总共保留了差不多 4GB 左右的内存。明明配置线程栈最大 256KB,为什么会出现 4GB 这么夸张的数字呢,到底哪里出了问题呢?

➜  ~ jcmd 24404 VM.native_memory summary
24404:
Native Memory Tracking:
Total: reserved=6635310KB, committed=5337110KB
-                 Java Heap (reserved=1048576KB, committed=1048576KB)(mmap: reserved=1048576KB, committed=1048576KB)
-                     Class (reserved=1066233KB, committed=15097KB)(classes #902)(malloc=9465KB #908)(mmap: reserved=1056768KB, committed=5632KB)
-                    Thread (reserved=4209797KB, committed=4209797KB)(thread #32)(stack: reserved=4209664KB, committed=4209664KB)(malloc=96KB #165)(arena=37KB #59)
-                      Code (reserved=249823KB, committed=2759KB)(malloc=223KB #730)(mmap: reserved=249600KB, committed=2536KB)
-                        GC (reserved=48700KB, committed=48700KB)(malloc=10384KB #135)(mmap: reserved=38316KB, committed=38316KB)
-                  Compiler (reserved=186KB, committed=186KB)(malloc=56KB #105)(arena=131KB #7)
-                  Internal (reserved=9693KB, committed=9693KB)(malloc=9661KB #2585)(mmap: reserved=32KB, committed=32KB)
-                    Symbol (reserved=2021KB, committed=2021KB)(malloc=1182KB #334)(arena=839KB #1)
-    Native Memory Tracking (reserved=85KB, committed=85KB)(malloc=5KB #53)(tracking overhead=80KB)
-               Arena Chunk (reserved=196KB, committed=196KB)(malloc=196KB)

重新以 VM.native_memory detail 参数运行 jcmd:

jcmd 24404 VM.native_memory detail

可以看到,有 16 个可疑线程,每一个线程保留了 262144KB 内存,也就是 256MB(通过下图红框可以看到,使用关键字 262144KB for Thread Stack from 搜索到了 16 个结果):

其实,ThreadStackSize 参数的单位是 KB,所以如果要设置线程栈 256KB,那么应该设置 256 而不是 256k。重新设置正确的参数后,使用 jcmd 再次验证下:

除了用于查看 NMT 外,jcmd 还有许多功能。可以通过 help,看到它的所有功能:

jcmd 24781 help

除了 jps、jinfo、jcmd、jstack、jstat、jconsole、jvisualvm 外,JDK 中还有一些工具,可以通过官方文档查看完整介绍。

官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/

 如果小假的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!

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

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

相关文章

动态规划简单题2

leetcode91题&#xff08;解码方法&#xff09; 分析题目&#xff1a; 1.这是一种解码&#xff0c;就是给多个数字组成的字符串&#xff0c;把这些数字解码成字母&#xff0c;看看一共有多少种 2.如果一个数字前有前导0就不合法&#xff0c;比如06&#xff0c;这与6不同&…

(007)Excel 公式的使用

文章目录 逻辑运算公式的参数常用函数引用方式引用工作表和工作簿表格的引用修改公式的计算时机区域交叉引用 逻辑运算 公式的参数 单元格引用&#xff1a;SUM(A1:A24)。字面值&#xff1a;SQRT(121)。字面文本字符串&#xff1a;PROPER(“john.f.smith”)。表达式&#xff1a…

Unity 和 Unreal Engine(UE) 两大主流游戏引擎的核心使用方法

以下是 Unity 和 Unreal Engine&#xff08;UE&#xff09; 两大主流游戏引擎的核心使用方法和对比分析&#xff0c;帮助开发者快速上手并根据项目需求选择合适工具&#xff1a; 一、Unity 使用指南 1. 安装与配置 安装&#xff1a;从 Unity Hub 下载&#xff0c;选择长期支持…

猜数字游戏:从数学原理到交互体验的完整设计指南

目录 猜数字游戏&#xff1a;从数学原理到交互体验的完整设计指南引言第一章 游戏数学原理1.1 均匀分布与随机生成1.2 最优猜测策略 第二章 游戏系统设计2.1 核心架构2.2 动态难度系统 第三章 交互设计细节3.1 输入验证系统3.2 渐进式提示机制 第四章 进阶功能设计4.1 智能辅导…

2025工业大模型白皮书 | 蚂蚁工厂北京航空航天大学联合出品

由蚂蚁工厂与北京航空航天大学联合发布的《2025工业大模型白皮书》是一部针对工业领域大模型技术发展的前瞻性研究报告。该白皮书系统梳理了工业大模型的技术演进、核心应用场景、关键挑战及未来发展趋势&#xff0c;旨在为制造业数字化转型提供理论支撑和实践指南。作为产学研…

JavaWeb:后端web基础(TomcatServletHTTP)

一、今日内容 二、Tomcat 介绍与使用 介绍 基本使用 小结 配置 配置 查找进程 三、Servlet 什么是Servlet 快速入门 需求 步骤 1.新建工程-模块&#xff08;Maven&#xff09; 2.修改打包方式-war 3.编写代码 /*** 可以选择继承HttpServlet*/ WebServlet("/hello&q…

构建现代分布式云架构的三大支柱:服务化、Service Mesh 与 Serverless

目录 前言1. 服务化架构模式&#xff1a;构建可扩展的基础单元1.1 服务化的定义与演进1.2 在分布式云中的价值1.3 面临的挑战 2. Service Mesh 架构&#xff1a;服务通信的治理中枢2.1 什么是 Service Mesh&#xff1f;2.2 功能与优势2.3 在分布式云中的角色2.4 落地难点 3. Se…

嵌入式C语言的运算符与输入输出

目录 1. 运算符 1.1 位运算符 1.1.1 位运算 ~ 1.1.2 位逻辑与 & 1.1.3 位逻辑或 | 1.1.4 位逻辑异或 ^ 1.1.5 位移位运算 1.1.6 将无符号位的某位快速置 1 1.2 三目运算符 1.3 逗号运算符 1.4 运算符优先级 2. 输出 2.1 字符输出函数 2.2 格式输出函数 2.3 字符…

IPD研学:76页页基于IPD思想-华为需求管理培训方案【附全文阅读】

适应人群 本方案适用于企业中参与产品研发、市场、销售、项目管理等部门的人员,尤其是负责需求管理工作的相关从业者;致力于提升产品竞争力,对优化需求管理流程、提高产品开发质量感兴趣的企业管理者;以及希望了解行业前沿需求管理方法,寻求突破和创新的相关人士。…

米酒的功能和优缺点

米酒&#xff08;又称甜酒、酒酿&#xff09;是一种以糯米或其他谷物为原料&#xff0c;经发酵制成的传统发酵饮品&#xff0c;酒精度较低&#xff08;通常1%-10%&#xff09;。以下是其功能、优点及缺点分析&#xff1a; 一、米酒的功能 营养补充 富含B族维生素&#xff08;B1…

注册登录页面项目

关系型数据库地址&#xff1a;C:\Users\ASUS\AppData\Local\Temp\HuaweiDevEcoStudioDatabases\rdb #注册页面register.ets import dataRdb from ohos.data.rdbconst STORE_CONFIG {name: weather4.db } const TABLE_NAME weather_info const SQL_CREATE_TABLE CREATE TAB…

yum源配置文件CentOS-Base.repo完整内容

1.CentOS Yum 源配置文件 CentOS-Base.repo 的完整内容示例&#xff1a; 以下是适用于不同版本 CentOS 系统的 CentOS-Base.repo 文件的标准模板。这些模板基于常见的国内镜像源&#xff08;如阿里云、清华大学等&#xff09;&#xff0c;可以帮助解决仓库配置失败的问题。 适…

深度学习基础--目标检测入门简介

博主简介&#xff1a;努力学习的22级本科生一枚 &#x1f31f;​ 博客主页&#xff1a;羊小猪~~-CSDN博客 内容简介&#xff1a;探索AI算法&#xff0c;C&#xff0c;go语言的世界&#xff1b;在迷茫中寻找光芒​&#x1f338;​ 往期回顾&#xff1a;yolov5基础–一步一步教…

雅思写作--70个高频表达

文章目录 1. learn new skills学生通过户外活动学到很多新技2. take immediate action to do各国采取有效行动以保护环境政府采取了必要行动以减少失业。你应该立即采取行动来解3. communication skills4. grow significantly5. have many advantages1. learn new skills “lea…

深入理解虚拟机与容器:原理、对比与应用场景分析

目录 前言1 虚拟机技术详解1.1 虚拟机的基本原理1.2 虚拟机的优势与局限 2 容器技术详解2.1 容器的运行机制2.2 容器的优势与局限 3 虚拟机与容器的核心差异对比3.1 架构对比3.2 启动速度与资源消耗3.3 安全性与隔离性3.4 兼容性与迁移性 4 实际应用场景分析4.1 适合使用虚拟机…

SQL Server数据库提权的几种方法——提权教程

SQL Server数据库提权的几种方法——提权教程 一、简介 在利用系统溢出漏洞没有效果的情况下,可以采用数据库进行提权。 数据库提权的前提条件: 1、服务器开启数据库服务 2、获取到最高权限用户密码 (除Access数据库外,其他数据库基本都存在数据库提权的可能) 二、使用x…

【第19节 信息加解密技术】

本章目录: 一、节概述二、知识详解1. 信息加密的基本分类2. 对称加密算法详解3. 非对称加密算法详解4. 密钥控制技术5. 密钥分发机制6. 公钥体系的密钥管理机制7. 密钥分类与生成 三、关键点提炼四、考试提示五、总结与建议 一、节概述 在信息安全体系中&#xff0c;信息加解密…

记录搭建自己的应用中心-需求看板搭建

记录搭建自己的应用中心-需求看板搭建 人员管理新增用户组织用户登录和操作看板状态看板任务通知任务详情 人员管理 由于不是所有人都有应用管理权限&#xff0c;所以额外做了一套应用登录权限&#xff0c;做了一个新的组织人员表&#xff0c;一个登录账户下的所有应用人员共享…

Java从入门到精通 - Java入门

Java 入门 此笔记参考黑马教程&#xff0c;仅学习使用&#xff0c;如有侵权&#xff0c;联系必删 文章目录 Java 入门01 Java快速入门1. Java 是什么&#xff1f;能干什么&#xff1f;1.1 Java 背景知识1.2 Java 能做什么&#xff1f;1.3 Java 技术体系 2. 如何使用 Java&…

MySQL-排序

介绍 在MySQL中&#xff0c;排序查询结果可以使用 ORDER BY 子句来实现。这个子句允许你根据一个或多个列进行排序&#xff0c;并且可以选择升序&#xff08;ASC&#xff09;或者降序&#xff08;DESC&#xff09;的方式。以下是基本的语法结构&#xff1a; SELECT column1, …