武培轩(公众号:武培轩)的《不可不知的7个JDK命令》这篇文章,短小精悍,介绍的这7个JDK命令非常实用,值得借鉴。
javap
使用javap可以查看Java字节码反编译的源文件,javap的命令格式如下:

下面来演示下用javap -c对代码进行反编译,首先写个HelloWorld类,如下:
public class HelloWorld {public static void main(String []args) {System.out.println("Hello World");}}
接着使用javap -c HelloWorld.class就可以反编译得到如下结果:
Compiled from "HelloWorld.java"public class HelloWorld {public HelloWorld();Code:0: aload_01: invokespecial #1 Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: getstatic #2 Field java/lang/System.out:Ljava/io/PrintStream;3: ldc #3 String Hello World5: invokevirtual #4 Method java/io/PrintStream.println:(Ljava/lang/String;)V8: return}
jps
jps是用来查询当前所有进程pid的,命令的用法如下图所示:

[root@wupx ~]# jps8825 spring-boot-0.0.1-SNAPSHOT.jar
使用jps -mlvV可以获取到这个进程的pid、jar包的名字以及JVM参数等。
[root@wupx ~]# jps -mlvV8825 root/spring-boot-0.0.1-SNAPSHOT.jar --server.port=8090 --logging.file=/root/log/spring-boot.log -Xmx1024m -Xms1024m
jstat
jstat主要用于监控JVM,主要是GC信息,在性能优化的时候经常用到,命令内容如下所示:

比如,上面我们通过jps查到的进程号8825,我们使用jstat -gc 8825来查看该进程的GC 信息:
[root@wupx ~]# jstat -gc 8825S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT65536.0 69120.0 0.0 160.0 10425344.0 1036247.8 21135360.0 19489859.7 84608.0 81123.8 9600.0 8834.1 99517 2070.459 0 0.000 2070.459
其中S0C表示当前Survivor0的容量,S1C表示当前Survivor1的容量,S0U表示当前Survivor0的利用率,S1U表示当前Survivor1的利用率,EC表示Eden的容量,EU表示Eden的利用率,OC表示老年代的容量,OU表示老年代的利用率,MC表示Metaspace的容量,MU表示Metaspace的利用率,CCSC表示类指针压缩空间容量,CCSU表示使用的类指针压缩空间,YGC表示新生代GC的次数,YGCT表示新生代GC的时间,FGC表示Full Gc的次数,FGCT表示Full GC的时间,GCT表示GC总时间。
每个对象都有一个指向它自身类的指针,_klass: 指向类的4字节指针,64位平台上_klass: 指向类的8字节的指针,为了节约这些空间,引入了类指针压缩空间。
jcmd
jcmd可以查看JVM信息,常用的命令内容如下:

[root@wupx ~]# jcmd 8825 help8825:The following commands are available:JFR.stopJFR.startJFR.dumpJFR.checkVM.native_memoryVM.check_commercial_featuresVM.unlock_commercial_featuresManagementAgent.stopManagementAgent.start_localManagementAgent.startVM.classloader_statsGC.rotate_logThread.printGC.class_statsGC.class_histogramGC.heap_dumpGC.finalizer_infoGC.heap_infoGC.run_finalizationGC.runVM.uptimeVM.dynlibsVM.flagsVM.system_propertiesVM.command_lineVM.versionhelp
下面我就选一个参数给大家举个例子,比如打印堆的信息,使用jcmd 8825 GC.heap_dump命令:
[root@wupx ~]# jcmd 8825 GC.heap_info8825:PSYoungGen total 628736K, used 41772K [0x0000000715a00000, 0x0000000746480000, 0x00000007c0000000)eden space 609792K, 4% used [0x0000000715a00000,0x00000007173d5478,0x000000073ad80000)from space 18944K, 80% used [0x000000073ad80000,0x000000073bc75e68,0x000000073c000000)to space 19968K, 0% used [0x0000000745100000,0x0000000745100000,0x0000000746480000)ParOldGen total 250880K, used 21756K [0x00000005c0e00000, 0x00000005d0300000, 0x0000000715a00000)object space 250880K, 8% used [0x00000005c0e00000,0x00000005c233f160,0x00000005d0300000)Metaspace used 44797K, capacity 45562K, committed 45824K, reserved 1089536Kclass space used 5669K, capacity 5832K, committed 5888K, reserved 1048576K
可以看出可以获取新生代、老年代、元空间、Eden、From Survivor以及To Survivor的大小和占比。
jmap
jmap打印出Java进程内存中Object的情况,或者将JVM中的堆以二进制输出成文本,命令内容如下:

[root@wupx ~]# jmap -heap 8825Attaching to process ID 8825, please wait...Debugger attached successfully.Server compiler detected.JVM version is 25.201-b09using thread-local object allocation.Parallel GC with 10 thread(s)Heap Configuration:MinHeapFreeRatio = 0MaxHeapFreeRatio = 100MaxHeapSize = 8575254528 (8178.0MB)NewSize = 178782208 (170.5MB)MaxNewSize = 2858418176 (2726.0MB)OldSize = 358088704 (341.5MB)NewRatio = 2SurvivorRatio = 8MetaspaceSize = 21807104 (20.796875MB)CompressedClassSpaceSize = 1073741824 (1024.0MB)MaxMetaspaceSize = 17592186044415 MBG1HeapRegionSize = 0 (0.0MB)Heap Usage:PS Young GenerationEden Space:capacity = 624427008 (595.5MB)used = 32083672 (30.597373962402344MB)free = 592343336 (564.9026260375977MB)5.138098062536078% usedFrom Space:capacity = 19398656 (18.5MB)used = 15687272 (14.960548400878906MB)free = 3711384 (3.5394515991210938MB)80.86782919394004% usedTo Space:capacity = 20447232 (19.5MB)used = 0 (0.0MB)free = 20447232 (19.5MB)0.0% usedPS Old Generationcapacity = 256901120 (245.0MB)used = 22278496 (21.246429443359375MB)free = 234622624 (223.75357055664062MB)8.672012017697703% used24741 interned Strings occupying 2987512 bytes.
首先会打印堆的一些相关配置,比如最大新生代、元空间的大小等;下面为堆的使用情况,包括新生代的Eden区、S0区、S1区以及老年代。
jmap还可以将堆的信息以文件的形式保存下来,相当于文件快照,执行 jmap -dump:live,format=b,file=heap.bin 8825命令:
[root@wupx ~]# jmap -dump:live,format=b,file=heap.bin 8825Dumping heap to root/heap.bin ...Heap dump file created
这个heap.bin可以使用jhat命令打开,是以html的形式展示的。
jhat
jhat分析Java堆的命令,可以将堆中对象以html的形式显示出来,支持对象查询语言OQL,命令内容如下:

[root@wupx ~]# jhat -port 9999 heap.binReading from heap.bin...Dump file created Tue May 12 22:31:55 CST 2020Snapshot read, resolving...Resolving 570997 objects...Chasing references, expect 114 dots..................................................................................................................Eliminating duplicate references..................................................................................................................Snapshot resolved.Started HTTP server on port 9999Server is ready.
执行完毕后,打开http://localhost:9999/就可以看到类的实例的堆占用情况,它是按照包名来分组的:

网页的底部还有许多Query方式:

下面以OQL为例,打开后是一个类似SQL查询的窗口,比如输入select s from java.lang.String s where s.value.length >= 100就可以查询字符串长度大于100的实例:

jstack
jstack是堆栈跟踪工具,主要用于打印给定进程pid的堆栈信息,一般在发生死锁或者CPU 100%的时候排查问题使用,可以去查询当前运行的线程以及线程的堆栈信息是什么情况,命令内容如下:

Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode):"Attach Listener" #51805777 daemon prio=9 os_prio=0 tid=0x00007f971c001000 nid=0x9cd6 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"DestroyJavaVM" #55 prio=5 os_prio=0 tid=0x00007f9fc8009800 nid=0x227a waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"http-nio-8111-Acceptor-0" #52 daemon prio=5 os_prio=0 tid=0x00007f96c40c5800 nid=0x2653 runnable [0x00007f97c0df9000]java.lang.Thread.State: RUNNABLEat sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)- locked <0x00007f982c6213c8> (a java.lang.Object)at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:455)at java.lang.Thread.run(Thread.java:748)"http-nio-8111-ClientPoller-0" #50 daemon prio=5 os_prio=0 tid=0x00007f9fc8e7e000 nid=0x2651 runnable [0x00007f97c21fb000]java.lang.Thread.State: RUNNABLEat sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)- locked <0x00007f982c622460> (a sun.nio.ch.Util$3)- locked <0x00007f982c622450> (a java.util.Collections$UnmodifiableSet)- locked <0x00007f982c622408> (a sun.nio.ch.EPollSelectorImpl)at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:787)at java.lang.Thread.run(Thread.java:748)"Service Thread" #17 daemon prio=9 os_prio=0 tid=0x00007f9fc8379000 nid=0x229d runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLE"C1 CompilerThread10" #15 daemon prio=9 os_prio=0 tid=0x00007f9fc8373800 nid=0x229b waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLE"VM Thread" os_prio=0 tid=0x00007f9fc831c000 nid=0x228d runnable"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f9fc801e800 nid=0x227b runnable"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f9fc8020800 nid=0x227c runnable"VM Periodic Task Thread" os_prio=0 tid=0x00007f9fc837e000 nid=0x229e waiting on conditionJNI global references: 357
因为内容比较多,截取了部分内容,可以看出会打印出线程的信息、状态以及堆栈,也会打印出GC Task的线程信息(ParallelGC属于并行收集器,默认为2个线程),从中可以分析出每个线程都在做什么,如果服务器CPU占用高,可以看有多少个线程处于RUNNABLE状态,一般是由于处于RUNNABLE状态的线程过多,导致CPU过高;如果很多线程处于TIMED_WAITING状态,理论上CPU占用不会很高。
本篇文章主要对JDK常用的内置命令javap、jps、jstat、jcmd、jmap、jhat、jstack进行了简单讲解,大家可以自己在本机进行实践。了解这些命令后会在死锁、CPU占用过高问题的排查、程序性能调优上会有很大的帮助。
参考:
https://docs.oracle.com/javase/8/docs/technotes/tools/index.html#basic
JDK内置命令工具
近期的热文:
《一个Full GC次数过多导致系统CPU 100%的案例排查》




