文章目录提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
- 01-概述
- 简单命令行工具
- 02 jps:查看正在运行的Java进程
- 基本情况
- 测试
- 基本语法
- options参数
- hostid参数
- 03-jstat:查看JVM统计信息
- 基本情况
- 基本语法
- option参数
- 先以【-class】为例,讲解jstat的用法
- 【interval参数】
- 【count参数】
- 【-t参数】
- 【-h参数】
- 各种option参数的用法
- JIT相关的
- 垃圾回收相关的
- jstat 如何排查OOM和内存泄漏
- 04-jinfo:实时查看和修改JVM配置参数
- 基本情况
- 基本语法
- 查看
- 修改
- 拓展
- 05-jmap:导出内存映像文件&内存使用情况
- 基本情况
- 基本语法
- 使用1:导出内存映像文件
- 使用2:显示堆内存相关信息
- 使用3:其他作用
- 小结
- 06-jhat:JDK自带堆分析工具(了解)
- 07-jstack:打印JVM中线程快照
- 基本情况
- 基本语法
- 参数
- 补充
- 08-jcmd:多功能命令行
- 基本情况
- 基本语法
01-概述 简单命令行工具
我们这里在windows下演示jps的使用
首先打开命令行窗口,输入jps
public class ScannerTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String info = scanner.next();
}
}
基本语法本地虚拟机ID和操作系统的进程ID是一致的。
可以通过 jps -help 来查看对应的参数信息
综合使用:
jps -l -m等价于jps -lm
如何将信息输出到同级文件中:
语法:命令 > 文件名称
例如:jps -l > a.txt
【-q 】
【-l 】
【-m】:输出虚拟机进程启动时传递给主类main()的参数
public class ScannerTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String info = scanner.next();
}
}
【-v】:列出虚拟机启动时的JVM参数。
补充:如果某Java进车关闭了默认开启的UsePerfData参数(即使用参数:-XX:-UsePerfData),那么jps命令(以及下面介绍的jstat)将无法探知该java进程。
其中vmid是进程id号,也就是jps之后看到的前面的号码,如下:
public class ScannerTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String info = scanner.next();
}
}
先以【-class】为例,讲解jstat的用法
【interval参数】
用于指定输出统计数据的周期,单位为毫秒。即:查询间隔
用于指定查询的总次数
【-t参数】interval和count只写一个的话,他会认为你是interval
可以在输出信息前加上一个Timestamp列,显示程序的运行时间。单位:秒
可以在周期性数据输出时,输出多少行数据后输出一个表头信息
- -compiler:显示JIT编译器编译过的方法、耗时等信息。
- -printcompilation :输出已经被JIT编译过的方法
-
-gc:显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等容量、已用空间、GC时间合计等信息。
-
-gccapacity:显示内容与-gc基相同,但输出主要关注Java堆各个区域使用的最大、最小空间
-
-gcutils:显示内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-
-gccause:与-gcutil功能相同,但是会额外输出导致最后一次或当前正在发生的GC产生的原因。
-
-gcnew:显示新生代GC状况
-
-gcnewcapacity:显示内容与-gcnew基本相同,但输出主要关注使用到的最大、最小空间
-
-gcold:显示老年代GC状况
-
-gcoldcapacity:显示内容与-gcold基本相同,输出主要关注使用到的最大、最小空间
-
-gcpermcapacity:显示永久代使用到的最大、最小空间
public class GCTest {
public static void main(String[] args) {
ArrayList 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();
}
}
}
}
jstat 如何排查OOM和内存泄漏
其实上述代码最终发生OOM
怎么查看呢?
我们重新启动上述代码
新启动的进程号为3956可以用 -gcutils监控jvm内存的占比情况:
我们也可以通过 -gccause打印产生gc的原因
经验:
(0.020-0.004)/(31.6-30.6)
- jinfo -sysprops 进程id :可以查看由System.getProperties()取得的参数
- jinfo -flags 进程id:查看曾经赋过值的一些参数
- jinfo -flag 参数名称 进程id:查看某个java进程的具体参数信息
-
针对boolean类型:jinfo -flag [+|-]参数名称 进程id
如果使用+号,那就可以让该参数起作用,否则使用-号就让该参数不起作用,具体例子如下:
-
针对非boolean类型:jinfo -flag 参数名称=参数值 进程id
修改只对当前进程有效
- java -XX:+PrintFlagsInitial:查看所有JVM参数启动的初始值
- java -XX:+PrintFlagsFinal:查看所有JVM参数的最终值
- java -参数名称:+PrintCommandLineFlags:查看那些已经被用户或者JVM设置过的详细的XX参数的名称和值
- -dump:生成Java堆转储快照:dump文件
特别的:-dump:live只保存堆中的存活对象
- -heap:输出整个堆空间的详细信息,包括GC的使用、堆配置信息,以及内存的使用信息等.
- -histo:输出堆中对象的同级信息,包括类、实例数量和合计容量
特别的:-histo:live只统计堆中的存活对象
- -permstat:以ClassLoader为统计口径输出永久代的内存状态信息
仅linux/solaris平台有效
- -finalizerinfo:显示在F-Queue中等待Finalizer线程执行finalize方法的对象
仅linux/solaris平台有效
- -F:当虚拟机进程对-dump选项没有任何响应时,可使用此选项强制执行生成dump文件
仅linux/solaris平台有效
- -h | -help:jamp工具使用的帮助命令
- -J < flag>:传递参数给jmap启动的jvm
1、手动的方式:
- jmap -dump:format=b,file=
< pid> - jmap -dump:live,format=b,file=
< pid>(生产生多使用这种,这个只保存存活的对象,因此较小一些)
public class GCTest {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
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();
}
}
}
}
注意:这个dump文件是二进制文件,因为我电脑安装了Jprofile,所以d盘生成的文件是这样子,它用记事本是打不开的。
打开是这种乱码。。。
dump文件的打开方式有很多种:jprofile、jconsole、MAT、以及后面说的指令jhat
另外,我们可以看出文件4是比较小的,因为jmap -dump:live,format=b,file=
2、自动的方式
手动的方式获取dump文件是随时随意的,但是自动的方式是在系统OOM时生成dump文件
public class GCTest {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
byte[] arr = new byte[1024 * 100];//100KB
list.add(arr);
try {
Thread.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
-
jmap -heap 进程id
jmap -heap 进程id只是时间点上的堆信息,而jstat后面可以添加参数,可以指定时间动态观察数据改变情况,而图形化界面工具,它们可以用图表的方式动态展示出相关信息,更加直观明了
Attaching to process ID 12792, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.152-b16 using thread-local object allocation. Parallel GC with 4 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 62914560 (60.0MB) NewSize = 20971520 (20.0MB) MaxNewSize = 20971520 (20.0MB) OldSize = 41943040 (40.0MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 16777216 (16.0MB) used = 2276224 (2.1707763671875MB) free = 14500992 (13.8292236328125MB) 13.567352294921875% used From Space: capacity = 2097152 (2.0MB) used = 0 (0.0MB) free = 2097152 (2.0MB) 0.0% used To Space: capacity = 2097152 (2.0MB) used = 0 (0.0MB) free = 2097152 (2.0MB) 0.0% used PS Old Generation capacity = 41943040 (40.0MB) used = 31980832 (30.499298095703125MB) free = 9962208 (9.500701904296875MB) 76.24824523925781% used 1766 interned Strings occupying 158112 bytes.
- jmap -histo 进程id:输出堆中对象的同级信息,包括类、实例数量和合计容量,也是这一时刻的内存中的对象信息
这两个指令仅linux/solaris平台有效,所以无法在windows操作平台上演示,并且使用比较小众,不在多说
- jmap -permstat :查看系统的ClassLoader信息
- jmap -finalizerinfo:查看堆积在finalizer队列中的对象
jhat命令在jdk9及其之后就被移除了,官方建议使用jvisualvm代替jhat,所以该指令只需简单了解一下即可
其中dumpfile代表dump文件的地址以及名称,例如:
options参数
举例如下:
public class ThreadDeadLock {
public static void main(String[] args) {
StringBuilder s1 = new StringBuilder();
StringBuilder s2 = new StringBuilder();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2022-05-07 20:03:01 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.152-b16 mixed mode): "DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x0000000002ca2800 nid=0x3074 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Thread-1" #12 prio=5 os_prio=0 tid=0x0000000018dc9800 nid=0x2980 waiting for monitor entry [0x0000000019a3f000] java.lang.Thread.State: BLOCKED (on object monitor) at com.atguigu.jstack.ThreadDeadLock$2.run(ThreadDeadLock.java:63) - waiting to lock <0x00000000d60b2630> (a java.lang.StringBuilder) - locked <0x00000000d60b2678> (a java.lang.StringBuilder) at java.lang.Thread.run(Thread.java:748) "Thread-0" #11 prio=5 os_prio=0 tid=0x0000000018dc7000 nid=0x3b94 waiting for monitor entry [0x000000001993e000] java.lang.Thread.State: BLOCKED (on object monitor) at com.atguigu.jstack.ThreadDeadLock$1.run(ThreadDeadLock.java:35) - waiting to lock <0x00000000d60b2678> (a java.lang.StringBuilder) - locked <0x00000000d60b2630> (a java.lang.StringBuilder) "Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000018cd6000 nid=0x3dc4 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000000018cd3000 nid=0x3048 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000000018c84000 nid=0x44bc waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000018c74000 nid=0x4080 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000018c71000 nid=0x47ac runnable [0x000000001933f000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked <0x00000000d6136878> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked <0x00000000d6136878> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64) "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x00000000178de800 nid=0x3488 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000018c38800 nid=0x29f8 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x00000000178ba000 nid=0x41c0 in Object.wait() [0x0000000018c2e000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000d5f08ec8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) - locked <0x00000000d5f08ec8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209) "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000002d92000 nid=0xa64 in Object.wait() [0x0000000018b2f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000d5f06b68> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x00000000d5f06b68> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) "VM Thread" os_prio=2 tid=0x0000000017898000 nid=0x44fc runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002cb8000 nid=0x370c runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002cb9800 nid=0x2134 runnable "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002cbb800 nid=0x1184 runnable "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002cbd000 nid=0x4028 runnable "VM Periodic Task Thread" os_prio=2 tid=0x0000000018d5a000 nid=0x4750 waiting on condition JNI global references: 30 Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x0000000018dcfb28 (object 0x00000000d60b2630, a java.lang.StringBuilder), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x0000000018dd5338 (object 0x00000000d60b2678, a java.lang.StringBuilder), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at com.atguigu.jstack.ThreadDeadLock$2.run(ThreadDeadLock.java:63) - waiting to lock <0x00000000d60b2630> (a java.lang.StringBuilder) - locked <0x00000000d60b2678> (a java.lang.StringBuilder) at java.lang.Thread.run(Thread.java:748) "Thread-0": at com.atguigu.jstack.ThreadDeadLock$1.run(ThreadDeadLock.java:35) - waiting to lock <0x00000000d60b2678> (a java.lang.StringBuilder) - locked <0x00000000d60b2630> (a java.lang.StringBuilder) Found 1 deadlock.
如果程序中出现死锁问题,jstack就会提示我们,并指出代码行数。
public class TreadSleepTest {
public static void main(String[] args) {
System.out.println("hello - 1");
try {
Thread.sleep(1000 * 60 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello - 2");
}
}
同样用jstack分析;
其实我们也可以自己在代码中追踪线程的路径,用代码进行监控线程
public class TreadSleepTest {
public static void main(String[] args) {
System.out.println("hello - 1");
try {
Thread.sleep(1000 * 60 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello - 2");
}
}
package com.atguigu.jstack;
import java.util.Map;
import java.util.Set;
public class ThreadDeadLock {
public static void main(String[] args) {
StringBuilder s1 = new StringBuilder();
StringBuilder s2 = new StringBuilder();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
Map all = Thread.getAllStackTraces();//追踪当前进程中的所有的线程
Set> entries = all.entrySet();
for(Map.Entry en : entries){
Thread t = en.getKey();
StackTraceElement[] v = en.getValue();
System.out.println("【Thread name is :" + t.getName() + "】");
for(StackTraceElement s : v){
System.out.println("t" + s.toString());
}
}
}
}).start();
}
}
"C:Program FilesJAVAjdk1.8.0_152binjava.exe" "-javaagent:D:BaiduNetdiskDownloadIntelliJ IDEA 2018.2.2libidea_rt.jar=60020:D:BaiduNetdiskDownloadIntelliJ IDEA 2018.2.2bin" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJAVAjdk1.8.0_152jrelibcharsets.jar;C:Program FilesJAVAjdk1.8.0_152jrelibdeploy.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextaccess-bridge-64.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextcldrdata.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextdnsns.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextjaccess.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextjfxrt.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextlocaledata.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextnashorn.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextsunec.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextsunjce_provider.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextsunmscapi.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextsunpkcs11.jar;C:Program FilesJAVAjdk1.8.0_152jrelibextzipfs.jar;C:Program FilesJAVAjdk1.8.0_152jrelibjavaws.jar;C:Program FilesJAVAjdk1.8.0_152jrelibjce.jar;C:Program FilesJAVAjdk1.8.0_152jrelibjfr.jar;C:Program FilesJAVAjdk1.8.0_152jrelibjfxswt.jar;C:Program FilesJAVAjdk1.8.0_152jrelibjsse.jar;C:Program FilesJAVAjdk1.8.0_152jrelibmanagement-agent.jar;C:Program FilesJAVAjdk1.8.0_152jrelibplugin.jar;C:Program FilesJAVAjdk1.8.0_152jrelibresources.jar;C:Program FilesJAVAjdk1.8.0_152jrelibrt.jar;D:ideaJVMDemo2outproductionchapter02" com.atguigu.jstack.ThreadDeadLock 【Thread name is :Thread-2】 java.lang.Thread.dumpThreads(Native Method) java.lang.Thread.getAllStackTraces(Thread.java:1610) com.atguigu.jstack.ThreadDeadLock$3.run(ThreadDeadLock.java:82) java.lang.Thread.run(Thread.java:748) 【Thread name is :Monitor Ctrl-Break】 java.net.SocketInputStream.socketRead0(Native Method) java.net.SocketInputStream.socketRead(SocketInputStream.java:116) java.net.SocketInputStream.read(SocketInputStream.java:171) java.net.SocketInputStream.read(SocketInputStream.java:141) sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) java.io.InputStreamReader.read(InputStreamReader.java:184) java.io.BufferedReader.fill(BufferedReader.java:161) java.io.BufferedReader.readLine(BufferedReader.java:324) java.io.BufferedReader.readLine(BufferedReader.java:389) com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64) 【Thread name is :Attach Listener】 【Thread name is :DestroyJavaVM】 【Thread name is :Thread-0】 com.atguigu.jstack.ThreadDeadLock$1.run(ThreadDeadLock.java:35) 【Thread name is :Finalizer】 java.lang.Object.wait(Native Method) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164) java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209) 【Thread name is :Reference Handler】 java.lang.Object.wait(Native Method) java.lang.Object.wait(Object.java:502) java.lang.ref.Reference.tryHandlePending(Reference.java:191) java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) 【Thread name is :Signal Dispatcher】 【Thread name is :Thread-1】 com.atguigu.jstack.ThreadDeadLock$2.run(ThreadDeadLock.java:63) java.lang.Thread.run(Thread.java:748)08-jcmd:多功能命令行 基本情况 基本语法
-
jcmd -l : 列出所有的JVM进程
-
jcmd 进程号 help:针对指定的进程,列出支持的所有具体命令
-
jcmd 进程号 具体命令:显示指定进程的指令命令的数据
-
根据以上命令来替换之前的那些操作:
Thread.print 可以替换 jstack指令
GC.class_histogram 可以替换 jmap中的-histo操作
GC.heap_dump 可以替换 jmap中的-dump操作
GC.run 可以查看GC的执行情况
VM.uptime 可以查看程序的总执行时间,可以替换jstat指令中的-t操作
VM.system_properties 可以替换 jinfo -sysprops 进程id
VM.flags 可以获取JVM的配置参数信息



