Jps
查看正在运行的 Java 进程
JPS(Java Process Staflus):显示指定系统内所有的 HotSpot 虚拟机进程,可用于查询正在运行的虚拟机进程。
语法格式:
jps [options] [hostid]
options 参数
-q:仅仅显示本地虚拟机唯一 id。不显示名称。
jps -q
-l:输入应用程序朱磊的全类名或如果执行的是 jar 包,则输出 jar 完整路径
jps -l
-m:输出虚拟机进程启动时传递给主类 main()的参数
jps -m
Jstat
jstat(JVM Statistics Monitoring Tool): 用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据。常用于检测垃圾回收问题以及内存泄漏问题
jstat [-命令选项] [jvmid] [间隔时间(毫秒)] [查询次数]
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
查看命令相关参数:
jstat -h 或者 jstat -help
类装载相关的
jstat -class PID
[root@node01 dump]# jstat -class 115269
Loaded Bytes Unloaded Bytes Time
5675 10472.5 0 0.0 6.20
- Loaded:加载class的数量
- Bytes:所占用空间大小
- Unloaded:未加载数量
- Bytes:未加载占用空间
- Time:时间
垃圾回收统计
jstat -gc pid 最常用,可以评估程序内存使用及GC压力整体情况
[root@node01 dump]# jstat -gc 115269
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
32768.0 32768.0 19648.5 0.0 196608.0 76785.3 262144.0 0.0 28800.0 27213.6 3712.0 3393.6 2 0.114 0 0.000 0.114
- S0C:第一个幸存区的大小,单位KB
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- OC:老年代大小
- OU:老年代使用大小
- MC:方法区大小(元空间)
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间,单位s
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间,单位s
- GCT:垃圾回收消耗总时间,单位s
其他的几个统计类型
堆内存统计
jstat -gccapacity pid
新生代垃圾回收统计
jstat -gcnew pid
新生代内存统计
jstat -gcnewcapacity pid
元数据空间统计
jstat -gcmetacapacity pid
堆内存统计
[root@node01 dump]# jstat -gccapacity 115269
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
262144.0 262144.0 262144.0 32768.0 32768.0 196608.0 262144.0 262144.0 262144.0 262144.0 0.0 1075200.0 28800.0 0.0 1048576.0 3712.0 2 0
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0C:第一个幸存区大小
- S1C:第二个幸存区的大小
- EC:伊甸园区的大小
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:当前老年代大小
- MCMN:最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代gc次数
- FGC:老年代GC次数
新生代垃圾回收统计
[root@node01 dump]# jstat -gcnew 115269
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
32768.0 32768.0 19648.5 0.0 2 15 16384.0 196608.0 120007.4 2 0.114
- S0C:第一个幸存区大小
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- TT:对象在新生代存活的次数
- MTT:对象在新生代存活的最大次数
- DSS:期望的幸存区大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间
新生代内存统计
[root@node01 dump]# jstat -gcnewcapacity 115269
NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
262144.0 262144.0 262144.0 32768.0 32768.0 32768.0 32768.0 196608.0 196608.0 2 0
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0CMX:最大幸存1区大小
- S0C:当前幸存1区大小
- S1CMX:最大幸存2区大小
- S1C:当前幸存2区大小
- ECMX:最大伊甸园区大小
- EC:当前伊甸园区大小
- YGC:年轻代垃圾回收次数
- FGC:老年代回收次数
老年代垃圾回收统计
[root@node01 dump]# jstat -gcold 115269
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
28800.0 27213.6 3712.0 3393.6 262144.0 0.0 2 0 0.000 0.114
- MC:方法区大小
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- OC:老年代大小
- OU:老年代使用大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
老年代内存统计
[root@node01 dump]# jstat -gcoldcapacity 115269
OGCMN OGCMX OGC OC YGC FGC FGCT GCT
262144.0 262144.0 262144.0 262144.0 2 0 0.000 0.114
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:老年代大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
元数据空间统计
[root@node01 dump]# jstat -gcmetacapacity 115269
MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT
0.0 1075200.0 28800.0 0.0 1048576.0 3712.0 2 0 0.000 0.114
- MCMN: 最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
总结垃圾回收统计
[root@node01 dump]# jstat -gcutil 115269
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
59.96 0.00 61.04 0.00 94.49 91.42 2 0.114 0 0.000 0.114
- S0:幸存1区当前使用比例
- S1:幸存2区当前使用比例
- E:伊甸园区使用比例
- O:老年代使用比例
- M:元数据区使用比例
- CCS:压缩使用比例
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
JVM编译方法统计
[root@node01 dump]# jstat -printcompilation 115269
Compiled Size Type Method
3122 41 1 org/apache/juli/logging/DirectJDKLog isDebugEnabled
- Compiled:最近编译方法的数量
- Size:最近编译方法的字节码数量
- Type:最近编译方法的编译类型。
- Method:方法名标识。
jinfo
jinfo(Configuralion Info for Java) : 查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数。实时查看和修改 JVM 配置参数
jinfo [ options ] pid
-sysprops
:可以查看由 System.getProperties()取得的参数。
-flags
:查看曾经赋过值的一些参数。
-flag
:查看某个 java 进程具体参数。
修改参数:
jinfo 不仅可以查看运行时某一个 Java 虚拟机参数的实际取值,甚至可以在运行时修改部分参数,并使之立即生效。参数只有被标记为 manageable 的 flag 可以被实时修改。其实,这个修改能力是极其有限的。
java -XX:+PrintFlagsFinal -version | grep manageable
可以查看被标记为 manageable 的参数。
针对 boolean 类型:
jinfo -flag [+-]具体参数 PID
针对非 boolean 类型:
jinfo -flag 具体参数=具体参数值 PID
java -XX:+PrintFlagsFinal PID:查看所有 JVM 参数的最终值。
java -XX:+PrintCommandLineFlags PID:查看已经被用户或者 JVM 设置过的详细参数名称和值。
jmap
jmap(JVM Memory Map) : 作用一方面是获取 dump 文件(堆转储快照文件,二进制文件),它还可以获取目标 Java 进程的内存相关信息,包括 Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等,导出内存映像文件/内存使用情况
基本语法
jmap [option] <pid>
jmap [option] <executable <core>
jmap [option] [server_id@]<remote server IP or hostname>
生成 Java 堆转储快照文件:dump
说明:
- 通常在写 Heap Dump 文件前会触发一次 Full GC , 所以 Heap Dump 文件里保存的都是 Full GC 后留下的对象信息。
- 由于生成 dump 文件比较耗时,大家需要耐心等待,尤其是大内存镜像生成的 dump 文件则需要耗费更长的时间来完成。
手动的方式
jmap -dump:format=b,file=<filename.hprof><pid> jmap -dump:live,format=b,file=<filename.hprof><pid> //存活对象自动的方式
当程序发生 OOM 退出系统时,一些瞬时信息都随着程序的终止而消失,而重现 OOM 问题往往比较困难或者耗时。此时若能在 OOM 时,自动导出 dump 文件就显得非常迫切。
这里介绍一种比较常用的取得堆快照文件的方法,即使用:在程序发生 OOM 时,导出应用程序的当前堆快照 :
- -XX:+HeapDumpOnOutOfMemoryError
- -XX:HeapDumpPath=./ (路径)
可以用jvisualvm命令工具导入该dump文件分析
查看各区大小
jmap -heap pid所有类型使用的内存
jmap -histo pidjmap -histo:live 14660 #查看当前存活的实例,执行过程中可能会触发一次full gc
由于
jmap
将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap 需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。
也就是说,由jmap
导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。
关于docker里面是有jmap不支持:
ERROR: ptrace(PTRACE_ATTACH, ..) failed for 12263: Operation not permitted
Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process: ptrace(PTRACE_ATTACH, ..) failed for 12263: Operation not permitted
sun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process: ptrace(PTRACE_
解决办法:
1.1 –security-opt seccomp=unconfined
简单暴力(不推荐),直接关闭 seccomp 配置。用法:
docker run --security-opt seccomp:unconfined ...
1.2 –cap-add=SYS_PTRACE
使用 --cap-add 明确添加指定功能:
docker run --cap-add=SYS_PTRACE ...
1.3 Docker Compose 的支持
Docker Compose 自 version 1.1.0 (2015-02-25) 起支持 cap_add。官方文档:cap_add, cap_drop。用法:
前面的 docker-compose.yml 改写后文件内容如下(相同内容部分就不重复贴了):
version: '2'
services:mysql:
cap_add:
- SYS_PTRACE
jhat
jhat(JVM Heap Analysis Topl) :Sun JDK 提供的
jhat
命令与jmap
命令搭配使用,用于分析jmap
生成的heap dump
文件(堆转储快照)。jhat
内置了一个微型的HTTP/HTML
服务器,生成dump
文件的分析结果后,用户可以在浏览器中查看分析结果(分析虚拟机转储快照信息)。使用了
jhat
命令,就启动了一个 http 服务,端口是 7000 , 即http://localhost:7000/
, 就可以在浏览器里分析。说明:jhat 命令在 JDK9、JDK10 中已经被删除,官方建议用
VisualVM
代替。
jhat [option] [dumpfile]
option 参数
jstack
jstack(JVM stlack Trace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照 : 当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。
生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。
这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用
jstack
显示各个线程调用的堆栈情况。
在
thread dump
中,要留意下面几种状态:
- 死锁,
Deadlock
- 等待资源,
Waiting on condition
- 等待获取监视器,
Waiting on monitor entry
- 阻塞,
Blocked
- 执行中,
Runnable
- 暂停,
Suspended
- 对象等待中,
Object.wait()
或TIMED_WAITING
- 停止,
Parked
基本语法
jstack [option] pid
jstack 管理远程进程的话,需要在远程程序的启动参数中增加:
- Djava.rmi.server.hostname=……
- Dcom.sun.management.jmxremote
- Dcom.sun.management.jmxremote.port=8888
- Dcom.sun.management.jmxremote.authenticate=false
- Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.port 为远程机器的JMX端口
-Djava.rmi.server.hostname 为远程机器IP
tomcat的JMX配置:在catalina.sh文件里的最后一个JAVA_OPTS的赋值语句下一行增加如下配置行
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=8888 -Djava.rmi.server.hostname=192.168.50.60 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
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");
}
}
还可以用jvisualvm自动检测死锁
option 参数
jcmd
在 JDK 1.7 以后,新增了一个命令行工具
jcmd
。
它是一个多功能的工具,可以用来实现前面除了jstat
之外所有命令的功能。比如:用它来导出堆、内存使用、查看Java 进程、导出线程信息、执行GC、JVM 运行时间等。
jcmd
拥有jmap
的大部分功能,并且在 Oracle 的官方网站上也推荐使用jcmd
命令代jmap
命令
jcmd -help
jcmd -l
:列出所有的 JVM 进程
jcmd pid help
:针对指定的进程,列出支持的所有命令
jcmd pid 具体命令
:显示指定进程的指令命令数据。
Arthas
Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具。支持 JDK6+, 采用命令行交互模式,可以方便的定位和诊断线上程序运行问题。Arthas 官方文档十分详细,详见:Arthas 用户文档 — Arthas 3.6.1 文档
Arthas使用场景
得益于 Arthas 强大且丰富的功能,让 Arthas 能做的事情超乎想象。下面仅仅列举几项常见的使用情况,更多的使用场景可以在熟悉了 Arthas 之后自行探索。
- 是否有一个全局视角来查看系统的运行状况?
- 为什么 CPU 又升高了,到底是哪里占用了 CPU ?
- 运行的多线程有死锁吗?有阻塞吗?
- 程序运行耗时很长,是哪里耗时比较长呢?如何监测呢?
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 有什么办法可以监控到 JVM 的实时运行状态?
# github下载arthas
wget https://alibaba.github.io/arthas/arthas-boot.jar
# 或者 Gitee 下载
wget https://arthas.gitee.io/arthas-boot.jar
用java -jar运行即可,可以识别机器上所有Java进程(我们这里之前已经运行了一个Arthas测试程序,代码见下方)
public class Arthas {
private static HashSet hashSet = new HashSet();
public static void main(String[] args) {
// 模拟 CPU 过高
cpuHigh();
// 模拟线程死锁
deadThread();
// 不断的向 hashSet 集合增加数据
addHashSetThread();
}
/**
* 不断的向 hashSet 集合添加数据
*/
public static void addHashSetThread() {
// 初始化常量
new Thread(() -> {
int count = 0;
while (true) {
try {
hashSet.add("count" + count);
Thread.sleep(1000);
count++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
public static void cpuHigh() {
new Thread(() -> {
while (true) {
}
}).start();
}
/**
* 死锁
*/
private static void deadThread() {
/** 创建资源 */
Object resourceA = new Object();
Object resourceB = new Object();
// 创建线程
Thread threadA = new Thread(() -> {
synchronized (resourceA) {
System.out.println(Thread.currentThread() + " get ResourceA");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resourceB");
synchronized (resourceB) {
System.out.println(Thread.currentThread() + " get resourceB");
}
}
});
Thread threadB = new Thread(() -> {
synchronized (resourceB) {
System.out.println(Thread.currentThread() + " get ResourceB");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting get resourceA");
synchronized (resourceA) {
System.out.println(Thread.currentThread() + " get resourceA");
}
}
});
threadA.start();
threadB.start();
}
}
选择进程序号1,进入进程信息操作
输入dashboard可以查看整个进程的运行情况,线程、内存、GC、运行环境信息:
输入thread可以查看线程详细情况
输入 thread加上线程ID 可以查看线程堆栈
输入 thread -b 可以查看线程死锁
输入 jad加类的全名 可以反编译,这样可以方便我们查看线上代码是否是正确的版本
使用 ognl 命令可以查看线上系统变量的值,甚至可以修改变量的值
更多命令使用可以用help命令查看,或查看文档:命令列表 — Arthas 3.6.1 文档