文章目录
本文中命令演示基于JDK8,不同的JDK版本可能存在差别
jps 查看虚拟机进程状况
概述
可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main()函数所在的类)名称以及这些进程的本地虚拟机唯一 ID(LVMID,Local Virtual Machine Identifier)。
如果某Java进程在启动是关闭了UsePerfData参数(即-XX:-UsePerfData),那么jps(以及下一小节介绍的jstat)将无法探知该Java进程。
官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html
基本语法
通过jps -help
查看命令格式。
选项 | 作用 |
---|---|
-q | 只输出LVMID,省略主类名 |
-m | 输出虚拟机进程启动时传递给主类main()函数的参数 |
-l | 输出主类的全名,如果进程执行的是jar包,则输出jar路径 |
-v | 输出虚拟机进程启动时的JVM参数 |
jstat 查看虚拟机统计信息
概述
可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据。
官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
基本语法
通过jstat -help
查看命令格式。
选项 | 作用 |
---|---|
-t | 可以在输出信息前加上Timestamp列,显示程序的运行时间。单位:秒 |
-h | 可以在周期性数据输出时,输出多少行数据后输出一个表头信息 |
vmid | 进程ID,通过jps -l 查询 |
interval | 用于指定输出统计数据的周期(查询间隔),单位:毫秒 |
count | 用于指定查询的总次数 |
通过jstat -options
查看具体执行命令。
选项 | 作用 |
---|---|
-class | 监视累加载、卸载数量、总空间以及类装载耗时 |
-gc | 监视Java堆状况,包括Eden区、2个survivor区、老年代、元空间等的容量,已用空间,垃圾收集时间合计等信息 |
-gccapacity | 监视内容和-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间 |
-gcutil | 监视内容和-gc基本相同,但输出主要关注已使用空间占总空间的半分比 |
-gccause | 和-gcutil功能一样,但是会额外输出导致上一次gc产生的原因 |
-gcnew | 监视新生代垃圾收集状况 |
-gcnewcapacity | 监视内容和-gcnew基本相同,输出主要关注使用到的最大、最小空间 |
-gcold | 监视老年代垃圾收集状况 |
-gcoldcapacity | 监视内容和-gcold基本相同,输出主要关注使用到的最大、最小空间 |
-gcmetacapacity | 输出元空间使用到的最大、最小空间 |
-compiler | 输出即时编译器编译过的方法、耗时等信息 |
-printcompilation | 输出已经被即时编译的方法 |
示例1
代码
public class MonitorTest {
public static void main(String[] args) {
try {
Thread.sleep(5 * 60_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
命令演示
执行-class
命令,-t
会显示Timestamp列,-h2
每隔两行打印下title,27547是通过jps -l
查到的LVMID
,10
是每隔10毫秒查询一次,5
是查询总次数。
示例2
代码
// 设置虚拟机运行参数 -Xm30m -Xmx30m -XX:SurvivorRatio=8
public static void main(String[] args) {
ArrayList<byte[]> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
byte[] arr = new byte[1024 * 100];//100KB
list.add(arr);
try {
Thread.sleep(120);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
命令演示
Titile对应的含义可以查看官方文档https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
,在此就不做翻译了。
经验
- 可以比较Java进程启动时间(Timestamp列)和总GC时间(GCT列),或是两次测量的间隔时间和总GC时间的增量,来计算出GC时间占运行时间的比例。如果比例超过20%则表明目前堆的压力较大,如果该比例超过90%,则说明堆里几乎没有可用空间,随时可能出现OOM。
- 可以多次运行jstat命令,取出每次运行后得到数据行中 OU 列的最小值,比较这个值,如果这些值呈上升趋势,则说明该Java程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄露。
jinfo 实时查看、修改虚拟机参数
概述
实时查看和调整虚拟机各项参数。
官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jinfo.html
基本语法
使用jinfo -help
查看命令格式。
命令格式描述的很清晰,下面简单翻译下。
- 查看
- jinfo -flag 具体参数名 PID,查看某个java进程的具体参数的值。
- jinfo -flags PID,查看赋过值的一些参数。
- jinfo -sysprops PID,查看在程序中可以通过System.getProperties()取到的参数。
- 修改
- 参数为boolean类型,jinfo -flag [+|-]具体参数 PID,[+|-]中
+
表示开启,-
表示关闭。- eg: jinfo -flag +HeapDumpOnOutOfMemoryError 28374
- 参数为赋值类型,jinfo -flag 具体参数=要设置的值 PID
- 具体可以修改的参数请看笔者整理的常用的JVM参数设置。
- 只有标记为manageable的参数才能修改,查看命令
java -XX:+PrintFlagsFinal -version | grep manageable
- 参数为boolean类型,jinfo -flag [+|-]具体参数 PID,[+|-]中
jmap 导出内存映像文件&查看堆内存使用情况
概述
用于生成堆转储快照(一般称为heapdump或dump文件),还可以查询finalize执行队列、Java堆和方法区的详细信息,如空间使用率、当前用的是哪种收集器等。
官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jmap.html
基本语法
使用jmap -help
查看命令格式。
选项 | 作用 |
---|---|
-heap | 显示整个堆的详细信息,包括GC的使用,堆配置信息,以及内存使用信息 |
-histo | 显示堆中对象统计信息,包括类、实例数量、合计容量 |
-clstats | 显示堆中类加载器统计信息 |
-finalizerinfo | 显示等待执行finalize方法的对象 |
-dump | 生成Java堆转出快照,jmap -dump:live,format=b,file=filename.hprof PID,live参数表示是否只dump存活的对象 |
-F | 当虚拟机对-dump选项没有响应时,可以使用此选项强制生成dump快照 |
示例
代码
// 设置虚拟机运行参数 -Xm30m -Xmx30m -XX:SurvivorRatio=8
public static void main(String[] args) {
ArrayList<byte[]> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
byte[] arr = new byte[1024 * 100];//100KB
list.add(arr);
try {
Thread.sleep(120);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
命令演示
上面是手动演示,通常情况下是在进程启动时配置好参数,-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<path/filename.hprof>
,这样在发生OOM是就会自动生成堆转储快照,便于后续分析排查问题。
jhat 虚拟机堆分析工具
和jmap配套使用,实际开发过程中不会直接在服务器上执行jhat,jhat执行会消耗大量内存,导致服务器不可用。如果是将导出的dump文件下载到本地进行分析,那就无需使用jhat了,使用有更强大功能的MAT导入dump文件进行分析。下一篇会讲解MAT如何使用。
jstack 生成虚拟机线程快照
概述
用于生成虚拟机当前时刻的线程快照(一般称为thread dump文件)。线程快照就是当前虚拟机内每一个线程正在执行的方法堆栈的集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因(如线程间死锁、死循环、请求外部资源导致的长时间挂起等),线程出现停顿时通过jstack来查看各个线程的调用堆栈情况。
在thread dump文件中需要留意以下几种状态,笔者转载的这篇文章讲解的比较详细 深入理解jstack日志
1. Deadlock(死锁)
2. Waiting on condition(等待资源)
3. Blocked(阻塞)
4. Waiting on monitor entry(等待获取锁)
5. Runnable(执行中)
6. Suspended(暂停)
7. TIMED_WWAITING 或 Object.wait()(对象等待中)
8. Parked(停止)
官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstack.html
基本语法
使用jstack -help
查看命令格式
选项 | 作用 |
---|---|
-F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
-l | 除堆栈外,显示关于锁的附加信息 |
-m | 如果调用到本地方法,可以显示C/C++的堆栈 |
示例1(死锁)
代码
public class JstackTestDeadLock {
private static String lockA = "锁对象A";
private static String lockB = "锁对象B";
public static void main(String[] args) {
new Thread() {
public void run() {
while (true) {
synchronized (lockA) {
System.out.println(this.getName() + ":得到lockA");
synchronized (lockB) {
System.out.println(this.getName() + ":得到lockB");
}
}
}
}
}.start();
new Thread() {
public void run() {
while (true) {
synchronized (lockB) {
System.out.println(this.getName() + ":得到lockB");
synchronized (lockA) {
System.out.println(this.getName() + ":得到lockA");
}
}
}
}
}.start();
}
}
命令演示
实例2(TIMED_WWAITING)
代码
public static void main(String[] args) {
System.out.println("hello");
try {
Thread.sleep(60_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("world");
}
命令演示
jstack -l PID