常用的JDK自带命令行工具
文章系本人原创,转载请保持完整性并注明出自 《四火的唠叨》
在 这里可以找得到这些工具的列表,虽然官网上免责声明为 “The tools described in this section are unsupported and experimental in nature and should be used with that in mind. They might not be available in future JDK versions.”,但实际上这些工具还是非常有用的,尤其可以站在JVM的角度来定位问题。我在此简单罗列一些常用的工具和命令,以及相应的执行结果示例。如果你经常和JVM打交道,最好对这些工具的常用命令熟记。
jstat,这个工具很强大,可以监测Java虚拟机GC多方面的状态,具体参数含义参见 此链接:
./jstat -gc 84012 1000 3 S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT 2112.0 2112.0 0.0 0.0 17024.0 0.0 63872.0 1319.9 21248.0 4728.1 743416 209.646 5 0.046 209.692 2112.0 2112.0 0.0 0.0 17024.0 0.0 63872.0 1319.9 21248.0 4728.1 743849 209.755 5 0.046 209.801 2112.0 2112.0 0.0 0.0 17024.0 0.0 63872.0 1319.9 21248.0 4728.1 744282 209.864 5 0.046 209.910
jmap,这大概是最常用的命令。下面这个命令不但可以列出类的实例数量,还有强制进行一次full GC的“副作用”,这样的副作用对于定位某些问题很有帮助,参见 《使用堆外内存》:
./jmap -histo:live 84012 num #instances #bytes class name ---------------------------------------------- 1: 824 1177656 [B 2: 8096 1106672 ...
使用-heap参数,则可以打印堆的使用情况:
./jmap -heap 84012 Attaching to process ID 84012, please wait... Debugger attached successfully. Server compiler detected. JVM version is 20.65-b04-462 using parallel threads in the new generation. using thread-local object allocation. Concurrent Mark-Sweep GC Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 132120576 (126.0MB) NewSize = 21757952 (20.75MB) MaxNewSize = 174456832 (166.375MB) OldSize = 65404928 (62.375MB) NewRatio = 7 SurvivorRatio = 8 PermSize = 21757952 (20.75MB) MaxPermSize = 85983232 (82.0MB) Heap Usage: New Generation (Eden + 1 Survivor Space): capacity = 19595264 (18.6875MB) used = 16785688 (16.008079528808594MB) free = 2809576 (2.6794204711914062MB) 85.66196403375837% used Eden Space: capacity = 17432576 (16.625MB) used = 16785688 (16.008079528808594MB) free = 646888 (0.6169204711914062MB) 96.28920017328477% used From Space: capacity = 2162688 (2.0625MB) used = 0 (0.0MB) free = 2162688 (2.0625MB) 0.0% used To Space: capacity = 2162688 (2.0625MB) used = 0 (0.0MB) free = 2162688 (2.0625MB) 0.0% used concurrent mark-sweep generation: capacity = 65404928 (62.375MB) used = 1390576 (1.3261566162109375MB) free = 64014352 (61.04884338378906MB) 2.126102791520541% used Perm Generation: capacity = 21757952 (20.75MB) used = 4852336 (4.6275482177734375MB) free = 16905616 (16.122451782226562MB) 22.301437194088855% used
使用-permstat参数,查看永久区:
./jmap -permstat 84012 Attaching to process ID 84012, please wait... Debugger attached successfully. Server compiler detected. JVM version is 20.65-b04-462 1239 intern Strings occupying 104312 bytes. finding class loader instances ..Warning: skipping invalid TLAB for thread t@59779 Warning: skipping invalid TLAB for thread t@59527 Warning: skipping invalid TLAB for thread t@59907 Warning: skipping invalid TLAB for thread t@60163 Warning: skipping invalid TLAB for thread t@60419 Warning: skipping invalid TLAB for thread t@60675 Finding object size using Printezis bits and skipping over... done. computing per loader stat ..done. please wait.. computing liveness...done. class_loader classes bytes parent_loader alive? type 590 3973048 null live 0x00000007f44cace0 0 0 null live sun/misc/Launcher$ExtClassLoader@0x00000007faff8a40 0x00000007f44c1478 8 143928 0x00000007f44cace0 live sun/misc/Launcher$AppClassLoader@0x00000007fb056e88 total = 3 598 4116976 N/A alive=3, dead=0 N/A
把内存中的堆dump成一个镜像文件:
./jmap -dump:live,format=b,file=/Users/xiongyi/Documents/dump.core 84012
jstack,线程堆栈打印。注意waiting to lock <xxx>在等待锁,比如进入临界区时;locked <xxx>表示当前同步操作,线程锁住了某资源;而waiting on <xxx>指的是在同步块内,wait方法的执行中暂时地释放了该锁的占用,等唤醒的时候需要重新获取:
./jstack 84012 2013-11-11 18:30:35 Full thread dump Java HotSpot(TM) 64-Bit Server VM (20.65-b04-462 mixed mode): "Attach Listener" daemon prio=9 tid=7ff64e206800 nid=0x117782000 waiting on condition [00000000] java.lang.Thread.State: RUNNABLE "Low Memory Detector" daemon prio=5 tid=7ff64c80f000 nid=0x117c96000 runnable [00000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" daemon prio=9 tid=7ff64c80e800 nid=0x117b93000 waiting on condition [00000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" daemon prio=9 tid=7ff64c80d800 nid=0x117a90000 waiting on condition [00000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" daemon prio=9 tid=7ff64c80d000 nid=0x11798d000 runnable [00000000] java.lang.Thread.State: RUNNABLE "Surrogate Locker Thread (Concurrent GC)" daemon prio=5 tid=7ff64c80c000 nid=0x11788a000 waiting on condition [00000000] java.lang.Thread.State: RUNNABLE "Finalizer" daemon prio=8 tid=7ff64e13d800 nid=0x11767f000 in Object.wait() [11767e000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <7f44c0ed0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) - locked <7f44c0ed0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:171) "Reference Handler" daemon prio=10 tid=7ff64e13c800 nid=0x11757c000 in Object.wait() [11757b000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <7f44c0018> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:485) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116) - locked <7f44c0018> (a java.lang.ref.Reference$Lock) "main" prio=5 tid=7ff64c800800 nid=0x10f709000 runnable [10f708000] java.lang.Thread.State: RUNNABLE at test.Test.main(Test.java:7) "VM Thread" prio=9 tid=7ff64e138000 nid=0x117479000 runnable "Gang worker#0 (Parallel GC Threads)" prio=9 tid=7ff64e000000 nid=0x112b0f000 runnable "Gang worker#1 (Parallel GC Threads)" prio=9 tid=7ff64e001000 nid=0x112c12000 runnable "Gang worker#2 (Parallel GC Threads)" prio=9 tid=7ff64e001800 nid=0x112d15000 runnable "Gang worker#3 (Parallel GC Threads)" prio=9 tid=7ff64e002000 nid=0x112e18000 runnable "Gang worker#4 (Parallel GC Threads)" prio=9 tid=7ff64e002800 nid=0x112f1b000 runnable "Gang worker#5 (Parallel GC Threads)" prio=9 tid=7ff64e003800 nid=0x11301e000 runnable "Gang worker#6 (Parallel GC Threads)" prio=9 tid=7ff64e004000 nid=0x113121000 runnable "Gang worker#7 (Parallel GC Threads)" prio=9 tid=7ff64e004800 nid=0x113224000 runnable "Concurrent Mark-Sweep GC Thread" prio=9 tid=7ff64e0e2000 nid=0x1170f0000 runnable "Gang worker#0 (Parallel CMS Threads)" prio=9 tid=7ff64e0e0800 nid=0x1166ea000 runnable "Gang worker#1 (Parallel CMS Threads)" prio=9 tid=7ff64e0e1800 nid=0x1167ed000 runnable "VM Periodic Task Thread" prio=10 tid=7ff64c820800 nid=0x117d99000 waiting on condition "Exception Catcher Thread" prio=10 tid=7ff64c801800 nid=0x10f936000 runnable JNI global references: 963
jinfo,可以打印JVM执行的参数信息,有一个非常大的作用在于,部分JVM参数在执行过程中是可以修改的,请参见这篇 《通过jinfo工具在full GC前后做heap dump》,通过jinfo改变参数HeapDumpBeforeFullGC和HeapDumpAfterFullGC,输出heap dump后,再改回来。
jhat,可以比对core文件之间的对象变化,支持对象查询语言(OQL),请参见 这里。
./jhat -stack true -refs true -port 8080 -baseline /xxx/dump-baseline.core -debug 1 /Users/xiongyi/Documents/dump-newer.core
javap,用于反编译class文件,对于JVM指令集,这里有 完整的文档。
javap -c -v ./Test.class Classfile xxx/Test.class Last modified Nov 11, 2013; size 441 bytes MD5 checksum 69488187cc8a8f166bc6dd0d517fb4cb Compiled from "Test.java" public class test.Test SourceFile: "Test.java" minor version: 0 major version: 50 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#16 // java/lang/Object."":()V #2 = Integer 1048576 #3 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream; #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/Object;)V #5 = Class #21 // test/Test #6 = Class #22 // java/lang/Object #7 = Utf8 #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 StackMapTable #14 = Utf8 SourceFile #15 = Utf8 Test.java #16 = NameAndType #7:#8 // "":()V #17 = Class #23 // java/lang/System #18 = NameAndType #24:#25 // out:Ljava/io/PrintStream; #19 = Class #26 // java/io/PrintStream #20 = NameAndType #27:#28 // println:(Ljava/lang/Object;)V #21 = Utf8 test/Test #22 = Utf8 java/lang/Object #23 = Utf8 java/lang/System #24 = Utf8 out #25 = Utf8 Ljava/io/PrintStream; #26 = Utf8 java/io/PrintStream #27 = Utf8 println #28 = Utf8 (Ljava/lang/Object;)V { public test.Test(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 4: 0 public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: ldc #2 // int 1048576 2: newarray byte 4: astore_1 5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 8: aload_1 9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 12: goto 0 LineNumberTable: line 7: 0 line 8: 5 line 9: 12 StackMapTable: number_of_entries = 1 frame_type = 0 /* same */ }
文章系本人原创,转载请保持完整性并注明出自 《四火的唠叨》