【4.2】jdk命令行工具
1、jps:虚拟机进程状况工具;
C:\Users\pacoson>jps -l // 输出主类名称
12272 sun.tools.jps.Jps
12736 chapter3.Page93
2808C:\Users\pacoson>jps -v // 查看虚拟机进程启动时的参数
12736 Page93 -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -Dfile.encoding=UTF-8
2808 -Dosgi.requiredJavaVersion=1.6 -Xms40m -Xmx512m -Dgrails.console.enable.interactive=false -Dgrails.console.enable.terminal=false -Djline.terminal=jline.UnsupportedTerminal -Dgrails.console.class=grails.build.logging.GrailsEclipseConsole
6072 Jps -Dapplication.home=D:\Java\jdk1.8.0_172 -Xms8mC:\Users\pacoson>jps // 列出正在运行的虚拟机进程
12736 Page93
2808
7992 Jps
补充:
jps -q 仅输出本地虚拟机唯一id, LVMID;
jps -m 输出虚拟机启动时传递给主类的main函数的参数;
jps可以通过rmi 协议查看开启了rmi服务的远程虚拟机进程状态 ;
2、jstat:虚拟机统计信息监视工具(java statistics monitoring tool)
C:\Users\pacoson>jstat -gcutil 12736 1000 20 // 每隔1000毫秒查看虚拟机12736的内存使用率,共查看20次S0 S1 E O M CCS YGC YGCT FGC FGCT GCT0.00 0.00 52.00 64.89 53.87 54.43 6 0.015 8 0.405 0.4210.00 0.00 52.00 64.89 53.87 54.43 6 0.015 8 0.405 0.4210.00 0.00 52.00 64.89 53.87 54.43 6 0.015 8 0.405 0.4210.00 0.00 52.00 64.89 53.87 54.43 6 0.015 8 0.405 0.421
S0=Survivor0, S=Suivivor1, E=Eden区域, O=Old区域, YGC=Young GC=收集年轻代次数=6, YGCT=0.015秒(年轻代收集耗时), FGC=Full GC=老年代收集次数=8, FGCT=Full GC Time=老年代收集耗时=0.421;
3、jinfo:java配置信息工具: 实时查看和调整虚拟机各项参数;
C:\Users\pacoson>jps // 查看本地java虚拟机id
12736 Page93
11496 Jps
2808C:\Users\pacoson>jinfo -flags //查看虚拟机参数
Usage:jinfo [option] <pid>(to connect to running process)jinfo [option] <executable <core>(to connect to a core file)jinfo [option] [server_id@]<remote server IP or hostname>(to connect to remote debug server)where <option> is one of:-flag <name> to print the value of the named VM flag-flag [+|-]<name> to enable or disable the named VM flag // +启用/-停用-flag <name>=<value> to set the named VM flag to the given value // 设置参数值-flags to print VM flags-sysprops to print Java system properties<no option> to print both of the above-h | -help to print this help messageC:\Users\pacoson>
C:\Users\pacoson>jinfo -flags 12736
Attaching to process ID 12736, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.172-b11
Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=10485760 -XX:OldSize=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Command line: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -Dfile.encoding=UTF-8
4、jmap:java内存映像工具,用于生成堆转储快照文件,一般称为heapdump 或 dump文件(堆转储快照文件);
使用-XX:+HeapDumpOnOutOfMemoryError参数,可以让jvm在oom异常出现之后自动生成dump文件;
jmap在windows平台下的功能是受限的;
jmap和jhat连用以查看虚拟机堆转储快照如下:
5、jhat:虚拟机堆转储快照分析工具;jvm Heap Analysis Tool,与jmap搭配使用;(不推荐使用jhat命令分析dump文件)
推荐的分析堆转储快照工具有:VisualVM, Eclipse Memory Analyzer, IBM HeapAnalyzer等工具;
C:\Users\pacoson>jmap -dump:format=b,file=eclipse.bin 12736 // 生成jvm12736的堆转储快照文件
Dumping heap to C:\Users\pacoson\eclipse.bin ...
File existsC:\Users\pacoson>
C:\Users\pacoson>jhat eclipse.bin // 启用内嵌web服务器分析eclpse.bin 文件(访问localhost:7000)
Reading from eclipse.bin...
Dump file created Tue May 07 23:43:59 CST 2019
Snapshot read, resolving...
Resolving 7066 objects...
Chasing references, expect 1 dots.
Eliminating duplicate references.
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
6、jstack:java堆栈跟踪工具
jstack=stack trace for java, 用于生成虚拟机当前时刻的线程快照(一般称为threaddump文件或 javacore文件);
线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程死锁,死循环,请求外部资源超时等;
C:\Users\pacoson>jstack -l 12736 // 查看12736进程堆栈信息
2019-05-12 20:55:41
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.172-b11 mixed mode):"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x000000001557f000 nid=0x794 runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"C1 CompilerThread3" #9 daemon prio=9 os_prio=2 tid=0x0000000015505000 nid=0x36e8 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"C2 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x00000000154fe000 nid=0x3ab4 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x00000000154f1000 nid=0x32b4 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x0000000015499000 nid=0x16e4 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000015498000 nid=0x578 waiting on condition [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000154ef800 nid=0x5b0 runnable [0x0000000000000000]java.lang.Thread.State: RUNNABLELocked ownable synchronizers:- None"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000015480800 nid=0x2c3c in Object.wait() [0x000000001595f000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x00000000fec08a78> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)- locked <0x00000000fec08a78> (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)Locked ownable synchronizers:- None"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001357d000 nid=0x103c in Object.wait() [0x000000001545f000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x00000000fec01158> (a java.lang.ref.Reference$Lock)at java.lang.Object.wait(Object.java:502)at java.lang.ref.Reference.tryHandlePending(Reference.java:191)- locked <0x00000000fec01158> (a java.lang.ref.Reference$Lock)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)Locked ownable synchronizers:- None"main" #1 prio=5 os_prio=0 tid=0x0000000002f2d800 nid=0x780 waiting on condition [0x0000000002a8f000]java.lang.Thread.State: TIMED_WAITING (sleeping)at java.lang.Thread.sleep(Native Method)at chapter3.Page93.minorGC(Page93.java:24)at chapter3.Page93.main(Page93.java:10)Locked ownable synchronizers:- None"VM Thread" os_prio=2 tid=0x0000000013578000 nid=0x3350 runnable"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002df7800 nid=0x3958 runnable"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002df9000 nid=0x4a7c runnable"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002dfa800 nid=0xcdc runnable"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002dfc000 nid=0x3a2c runnable"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002dfe800 nid=0x3b30 runnable"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002dff800 nid=0x3a0 runnable"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002e02800 nid=0x2a0 runnable"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002e05000 nid=0x1ed4 runnable"VM Periodic Task Thread" os_prio=2 tid=0x00000000155ae000 nid=0x35e8 waiting on conditionJNI global references: 5
可以通过调用 Thread.getAllStackTraces() 方法完成 jstack的大部分功能;
【荔枝】
/*** Thread.getAllStackTraces获取虚拟机中所有线程的StackTraceElement对象* , 模拟jstack的大部分功能 */
public class Page111 {public static void main(String[] args) {for (int i = 0; i < 5; i++) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(new Random(17).nextInt(10)); } }).start(); // // 启动线程 }for (int i = 0; i < 3600; i++) {System.out.println("================");try {Thread.sleep(1000);/* Thread.getAllStackTraces获取虚拟机中所有线程的StackTraceElement对象 */Map<Thread, StackTraceElement[]> stackTracks = Thread.getAllStackTraces();for (Map.Entry<Thread, StackTraceElement[]> entry : stackTracks.entrySet()) {System.out.println("key = " + entry.getKey().getName() + ", value = " + entry.getValue());StackTraceElement[] elements = entry.getValue();int index = 1;for (StackTraceElement e : elements) {System.out.println("value[" + index++ +"] = " + e);}}} catch (InterruptedException e) {e.printStackTrace();}}}
}
// Thread.getAllStackTraces获取虚拟机中所有线程的StackTraceElement对象,线程方法堆栈信息
key = Finalizer, value = [Ljava.lang.StackTraceElement;@5c647e05
value[1] = java.lang.Object.wait(Native Method)
value[2] = java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
value[3] = java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
value[4] = java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
key = Attach Listener, value = [Ljava.lang.StackTraceElement;@33909752
key = Signal Dispatcher, value = [Ljava.lang.StackTraceElement;@55f96302
key = Reference Handler, value = [Ljava.lang.StackTraceElement;@3d4eac69
value[1] = java.lang.Object.wait(Native Method)
value[2] = java.lang.Object.wait(Object.java:502)
value[3] = java.lang.ref.Reference.tryHandlePending(Reference.java:191)
value[4] = java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
key = main, value = [Ljava.lang.StackTraceElement;@42a57993
value[1] = java.lang.Thread.dumpThreads(Native Method)
value[2] = java.lang.Thread.getAllStackTraces(Thread.java:1610)
value[3] = chapter4.Page111.main(Page111.java:25)
7、HSDIS:JIT生成代码反汇编
HSDIS是一个sun官方推荐的hotspot虚拟机 JIT编译代码的反汇编插件,包含在hotspot虚拟机的源码之中,但没有提供编译后的程序。
作用是让hotspot的-XX:+PrintAssembly 指令调用它来吧动态生成的本地代码还原为汇编代码输出,同时还生成了大量有价值的注释;
【4.3】JDK的可视化工具
JConsole和VisualVM;JConsole是虚拟机监控工具, 而VisualVM是多合一故障处理工具;
1、JConsole:java监视与管理控制台;基于JMX的可视化监视,管理工具;
1.1、启动JConsole;
【JConsole监控荔枝】
/*** JConsole 监控jvm内存 * vm params: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8*/
public class Page114JConsoleTest {private static final int _1MB = 1024 * 1024;public static void main(String[] args) {for (int i = 0; i < 3600; i++) {new Thread(new Runnable() {@Overridepublic void run() {byte[] allocation1 = new byte[1 * _1MB]; byte[] allocation2 = new byte[2 * _1MB];byte[] allocation4 = new byte[4 * _1MB];} }).start(); // // 启动线程try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace(); }}}
}
1.2、内存监控
/*** JConsole监视代码 * jvm params: -Xms100m -Xmx100m -XX:+UseSerialGC* @author tang rong* 2019/05/12 */
public class Page117 {public static void main(String[] args) throws Exception {fillHeap(1000);}public static void fillHeap(int num) throws InterruptedException {List<OOMObject> list = new ArrayList<>(); for (int i=0; i<num; i++) {System.out.println(i);/* 稍作延时,令监视曲线的变化更加明显 */Thread.sleep(50);list.add(new OOMObject());}System.gc(); }static class OOMObject {byte[] placeholder = new byte[64 * 1024];}
}
1.3、线程监控
线程长时间停顿的主要原因有: 等待外部资源(数据库连接,网络资源,设备资源等),死循环,锁等待(活锁和死锁);
【荔枝-线程等待演示代码】
/*** 线程等待演示代码* @author tr* 2019/05/12 */
public class Page119 {public static void createBusyThread() {new Thread(new Runnable() {@Overridepublic void run() {while (true) // 死循环;}}, "testBusyThread").start();}public static void createLockThread(final Object lock) {new Thread(new Runnable() {@Overridepublic void run() {synchronized (lock) {try {lock.wait(); // 线程等待 } catch (InterruptedException e) {e.printStackTrace();}}}}, "testLockThread").start(); }public static void main(String[] args) throws Exception {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));br.readLine();createBusyThread(); // 死循环br.readLine();Object obj = new Object(); createLockThread(obj); // 线程等待}
}
【荔枝-线程死锁等待演示代码】
/*** 线程死锁等待演示 * @author tr* 2019/05/12 */
public class SynAddRunnable implements Runnable {int a, b; public SynAddRunnable(int a, int b) {this.a = a;this.b = b; }@Override public void run() {synchronized (Integer.valueOf(a)) {synchronized (Integer.valueOf(b)) {System.out.println(a+b);}}}public static void main(String[] args) {for (int i=0; i<100; i++) {System.out.println("i = " + i);new Thread(new SynAddRunnable(1, 2)).start(); // 死锁, 线程1拿到了a锁,准备拿b锁,但线程2拿到了b锁且没有释放;new Thread(new SynAddRunnable(2, 1)).start(); // 死锁,线程2拿到了b锁,准备拿a锁,但线程1拿到了a锁且没有释放;} }
}
出现死锁后,查看线程列表中被阻塞的线程;
【4.3.2】VisualVM:多合一故障处理工具(jvisualvm.exe)
1、VisualVM可以做到:
显示jvm进程配置信息,环境信息(jps, jinfo);
监视cpu, gc,堆,方法区以及线程信息(jstat, jstack);
dump以及分析堆转储快照(jmap,jhat);
方法级的程序运行性能分析,找出被调用最多,运行时间最长的方法;
离线程序快照:收集程序的运行时配置,线程dump,内存dump等信息建立一个快照,可以将快照发送给开发者;
其他plugin的无限可能性;
【补充】VisualVM的兼容性
如果不给VisualVM安装插件,就放弃了其最精华的功能;
2、生成、浏览堆转储快照-dump文件
点击堆dump;
3、分析程序性能:profiler页签中分析;
注意: profiling堆程序运行性能有很大影响,一般不在生产环境做;
4、BTrace动态日志跟踪
4.1、BTrace的作用是:在不停止目标程序运行的前提下,通过hotspot虚拟机的hotswap技术动态加入原本并不存在的调试代码(如日志信息);
补充:hotswap技术,即代码热替换技术, hotspot虚拟机允许在不停止运行的情况下,更新已经加载的类的代码;
/*** BTrace 跟踪演示 * @date 2019/05/13*/
public class BTraceTest {public int add(int a, int b) {return a + b; }public static void main(String[] args) throws IOException {BTraceTest test = new BTraceTest();BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));for (int i = 0; i < 10; i++) { reader.readLine();int a = (int) Math.round(Math.random() * 1000);int b = (int) Math.round(Math.random() * 1000);System.out.println("test.add(a, b) = " + test.add(a, b));}}
}
trace script
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;@BTrace
public class TracingScript {/* put your code here */@OnMethod(clazz = "chapter4.BTraceTest",method = "add",location = @Location(Kind.RETURN))public static void func(@Self chapter4.BTraceTest instance,int a,int b,@Return int result){println("调用堆栈:");jstack();println(strcat("方法参数A:",str(a)));println(strcat("方法参数B:",str(b)));println(strcat("方法结果:",str(result))); }
}
BTrace的用法包括: 打印调用堆栈,参数,返回值只是最基本的应用,还可以进行性能监视,定位连接泄漏和内存泄漏,解决多线程竞争问题等;