概述
前言
在线上我们经常会遇见如下几个问题:
- 内存泄露;
- 某个进程突然 CPU 飙升;
- 线程死锁;
- 响应变慢。
如果遇到了以上这种问题,在 线下环境 可以有各种 可视化的本地工具 支持查看。但是一旦到 线上环境,就没有这么多的 本地调试工具 支持,我们该如何基于 监控工具 来进行定位问题?
我们一般会基于 数据收集 来定位问题,而数据的收集离不开 监控工具 的处理,比如:运行日志、异常堆栈、GC 日志、线程快照、堆内存快照 等。为了解决以上问题,我们常用的 JVM 性能调优监控工具 大致有:jps、jstat、jstack、jmap、jhat、hprof、jinfo。
1. jps进程监控工具
jps 是用于查看有权访问的 hotspot 虚拟机 的进程。当未指定 hostid 时,默认查看 本机 jvm 进程,否则查看指定的 hostid 机器上的 jvm 进程,此时 hostid 所指机器必须开启 jstatd 服务。
jps 可以列出 jvm 进程 lvmid,主类类名,main 函数参数, jvm 参数,jar 名称等信息。
命令格式如下:
usage: jps [-help]
jps [-q] [-mlvV] [<hostid>]
Definitions:
<hostid>: <hostname>[:<port>]
参数含义如下:
-q: 不输出 类名称、Jar 名称 和传入 main 方法的 参数;
-l: 输出 main 类或 Jar 的 全限定名称;
-m: 输出传入 main 方法的 参数;
-v: 输出传入 JVM 的参数。
2. jinfo配置信息查看工具
jinfo(JVM Configuration info)这个命令作用是实时查看和调整 虚拟机运行参数。之前的 jps -v 命令只能查看到显示 指定的参数,如果想要查看 未显示 的参数的值就要使用 jinfo 命令。
Usage:
jinfo [option] <pid>
(to connect to running process)
jinfo [option] <executable <core>
(to connect to a core file)
jinfo [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)
参数含义如下:
pid:本地 jvm 服务的进程 ID;
executable core:打印 堆栈跟踪 的核心文件;
remote server IP/hostname:远程 debug 服务的 主机名 或 IP 地址;
server id:远程 debug 服务的 进程 ID。
参数选项说明如下:
参数 | 参数含义 |
---|---|
flag | 输出指定 args 参数的值 |
flags | 不需要 args 参数,输出所有 JVM 参数的值 |
sysprops | 输出系统属性,等同于 System.getProperties() |
3. jstat信息统计监控工具
jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下:
jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]
注意:使用的jdk版本是jdk8.
垃圾回收统计 -gc
jstat -gc pid 最常用,可以评估程序内存使用及GC压力整体情况
参数 | 参数含义 |
---|---|
S0C | 第一个幸存区的大小 |
S1C | 第二个幸存区的大小 |
S0U | 第一个幸存区的使用大小 |
S1U | 第二个幸存区的使用大小 |
EC | 伊甸园区的大小 |
EU | 伊甸园区的使用大小 |
OC | 老年代大小 |
OU | 老年代使用大小 |
MC | 方法区大小(元空间) |
MU | 方法区使用大小 |
CCSC | 压缩类空间大小 |
CCSU | 压缩类空间使用大小 |
YGC | 年轻代垃圾回收次数 |
YGCT | 年轻代垃圾回收消耗时间,单位sFGC:老年代垃圾回收次数 |
FGCT | 老年代垃圾回收消耗时间,单位s |
GCT | 垃圾回收消耗总时间,单位s |
堆内存统计 -gccapacity
jstat -gccapacity pid
参数 | 参数含义 |
---|---|
NGCMN | 新生代最小容量 |
NGCMX | 新生代最大容量 |
NGC | 当前新生代容量 |
S0C | 第一个幸存区大小 |
S1C | 第二个幸存区的大小 |
EC | 伊甸园区的大小 |
OGCMN | 老年代最小容量 |
OGCMX | 老年代最大容量 |
OGC | 当前老年代大小 |
OC | 当前老年代大小 |
MCMN | 最小元数据容量 |
MCMX | 最大元数据容量 |
MC | 当前元数据空间大小 |
CCSMN | 最小压缩类空间大小 |
CCSMX | 最大压缩类空间大小 |
CCSC | 当前压缩类空间大小 |
YGC | 年轻代gc次数 |
FGC | 老年代GC次数 |
新生代垃圾回收统计 -gcnew
jstat -gcnew pid
参数 | 参数含义 |
---|---|
S0C | 第一个幸存区的大小 |
S1C | 第二个幸存区的大小 |
S0U | 第一个幸存区的使用大小 |
S1U | 第二个幸存区的使用大小 |
TT | 对象在新生代存活的次数 |
MTT | 对象在新生代存活的最大次数 |
DSS | 期望的幸存区大小 |
EC | 伊甸园区的大小 |
EU | 伊甸园区的使用大小 |
YGC | 年轻代垃圾回收次数 |
YGCT | 年轻代垃圾回收消耗时间 |
新生代内存统计 -gcnewcapacity
jstat -gcnewcapacity pid
参数 | 参数含义 |
---|---|
NGCMN | 新生代最小容量 |
NGCMX | 新生代最大容量 |
NGC | 当前新生代容量 |
S0CMX | 最大幸存1区大小 |
S0C | 当前幸存1区大小 |
S1CMX | 最大幸存2区大小 |
S1C | 当前幸存2区大小 |
ECMX | 最大伊甸园区大小 |
EC | 当前伊甸园区大小 |
YGC | 年轻代垃圾回收次数FGC:老年代回收次数 |
老年代垃圾回收统计 -gcold
jstat -gcold pid
参数 | 参数含义 |
---|---|
MC | 方法区大小 |
MU | 方法区使用大小 |
CCSC | 压缩类空间大小 |
CCSU | 压缩类空间使用大小 |
OC | 老年代大小 |
OU | 老年代使用大小 |
YGC | 年轻代垃圾回收次数 |
FGC | 老年代垃圾回收次数 |
FGCT | 老年代垃圾回收消耗时间 |
GCT | 垃圾回收消耗总时间 |
元数据空间统计 -gcmetacapacity
jstat -gcmetacapacity pid
参数 | 参数含义 |
---|---|
MCMN | 最小元数据容量 |
MCMX | 最大元数据容量 |
MC | 当前元数据空间大小 |
CCSMN | 最小压缩类空间大小 |
CCSMX | 最大压缩类空间大小 |
CCSC | 当前压缩类空间大小 |
YGC | 年轻代垃圾回收次数 |
FGC | 老年代垃圾回收次数 |
FGCT | 老年代垃圾回收消耗时间 |
GCT | 垃圾回收消耗总时间 |
各个内存的使用占比,垃圾回收时间和回收次数 -gcutil
jstat -gcutil pid
参数 | 参数含义 |
---|---|
S0 | 幸存1区当前使用比例 |
S1 | 幸存2区当前使用比例 |
E | 伊甸园区使用比例 |
O | 老年代使用比例 |
M | 元数据区使用比例 |
CCS | 压缩使用比例 |
YGC | 年轻代垃圾回收次数 |
FGC | 老年代垃圾回收次数 |
FGCT | 老年代垃圾回收消耗时间 |
GCT | 垃圾回收消耗总时间 |
4. jmap堆内存统计工具
jmap (JVM Memory Map) 命令用来查看 堆内存 使用状况,一般结合 jhat 使用,用于生成 heap dump 文件。jmap 不仅能生成 dump 文件,还可以查询 finalize 执行队列、Java 堆 和 元空间 metaspace 的详细信息,如当前 使用率、当前使用的是哪种 收集器 等等。
如果不使用这个命令,还可以使用 -XX:+HeapDumpOnOutOfMemoryError 参数来让虚拟机出现 OOM 的时候,自动生成 dump 文件。
实例个数以及占用内存大小 -histo
- num:序号
- instances:实例数量
- bytes:占用空间大小
- class name:类名称,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]
堆信息 -heap
参数 | 对应启动参数 | 参数含义 |
---|---|---|
MinHeapFreeRatio | -XX:MinHeapFreeRatio | JVM堆最小空闲比率(default 40) |
MaxHeapFreeRatio | -XX:MaxHeapFreeRatio | JVM堆最大空闲比率(default 70) |
MaxHeapSize | XX:Xmx | JVM堆的最大大小 |
NewSize | -XX:NewSize | JVM堆新生代的默认(初始化)大小 |
MaxNewSize | -XX:MaxNewSize | JVM堆新生代的最大大小 |
OldSize | -XX:OldSize | JVM堆老年代的默认(初始化)大小 |
NewRatio | -XX:NewRatio | JVM堆新生代和老年代的大小比例 |
SurvivorRatio | -XX:SurvivorRatio | JVM堆年轻代中Eden区与Survivor区的大小比值 |
MetaspaceSize | -XX:MetaspaceSize | JVM元空间(metaspace)初始化大小 |
MaxMetaspaceSize | -XX:MaxMetaspaceSize | JVM元空间(metaspace)最大大小 |
CompressedClass SpaceSize | -XX:CompressedClass SpaceSize | JVM类指针压缩空间大小, 默认为1G |
G1HeapRegionSize | -XX:G1HeapRegionSize | 使用G1垃圾回收器时单个Region的大小,取值为1M至32M |
堆内存 dump
dump 用于导出内存转储快照。常用的方式是通过 jmap 把进程 内存使用情况 dump 到文件中,再用 jhat 分析查看。jmap 进行 dump 的命令格式如下:
jmap -dump:format=b,file=dumpFileName
public class OOMTest {
public static List<Object> list = new ArrayList<>();
// JVM设置
// ‐Xms10M ‐Xmx10M ‐XX:+PrintGCDetails ‐XX:+HeapDumpOnOutOfMemoryError ‐XX:HeapDumpPath=E:jvm.dump
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
int i = 0;
int j = 0;
while (true) {
list.add(new User(i++, UUID.randomUUID().toString()));
new User(j--, UUID.randomUUID().toString());
}
}
}
我们也可以用 jvisualvm 导入dump文件来分析
5.jstack堆栈跟踪工具
jstack 用于生成 java 虚拟机当前时刻的 线程快照。线程快照 是当前 java 虚拟机内 每一条线程 正在执行的 方法堆栈 的 集合。生成线程快照的主要目的是定位线程出现 长时间停顿 的原因,如 线程间死锁、死循环、请求外部资源 导致的 长时间等待 等等。
线程出现 停顿 的时候,通过 jstack 来查看 各个线程 的 调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。如果 java 程序 崩溃 生成 core 文件,jstack 工具可以通过 core 文件获取 java stack 和 native stack 的信息,从而定位程序崩溃的原因。
系统线程状态
在 dump 文件里,值得关注的 线程状态 有:
- 死锁:Deadlock(重点关注)
- 执行中:Runnable
- 等待资源:Waiting on condition(重点关注)
- 等待获取监视器:Waiting on monitor entry(重点关注)
- 暂停:Suspended
- 对象等待中:Object.wait() 或 TIMED_WAITING
- 阻塞:Blocked(重点关注)
- 停止:Parked
用jstack查找死锁,见如下示例
public class DeadLockTest {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
try {
System.out.println("thread1 begin");
Thread.sleep(5000);
} catch (InterruptedException e) {
}
synchronized (lock2) {
System.out.println("thread1 end");
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
try {
System.out.println("thread2 begin");
Thread.sleep(5000);
} catch (InterruptedException e) {
}
synchronized (lock1) {
System.out.println("thread2 end");
}
}
}).start();
System.out.println("main thread end");
}
}
“Thread-1” 线程名
prio=5 优先级=5
tid=0x000000001fa9e000 线程id
nid=0x2d64 线程对应的本地线程标识nid
runnable 线程状态
最后
以上就是积极短靴为你收集整理的JVM(六) - JVM性能调优监控工具的全部内容,希望文章能够帮你解决JVM(六) - JVM性能调优监控工具所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复