jvm高级特性第4章-虚拟机性能监控与故障处理工具

【4.2】jdk命令行工具

1、jps:虚拟机进程状况工具;

C:\Users\pacoson>jps -l // 输出主类名称
12272 sun.tools.jps.Jps
12736 chapter3.Page93
2808

C:\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 -Xms8m

C:\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     GCT
  0.00   0.00  52.00  64.89  53.87  54.43      6    0.015     8    0.405    0.421
  0.00   0.00  52.00  64.89  53.87  54.43      6    0.015     8    0.405    0.421
  0.00   0.00  52.00  64.89  53.87  54.43      6    0.015     8    0.405    0.421
  0.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
2808

C:\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 message

C:\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 exists

C:\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: RUNNABLE

   Locked ownable synchronizers:
        - None

"C1 CompilerThread3" #9 daemon prio=9 os_prio=2 tid=0x0000000015505000 nid=0x36e8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C2 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x00000000154fe000 nid=0x3ab4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x00000000154f1000 nid=0x32b4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x0000000015499000 nid=0x16e4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000015498000 nid=0x578 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000154ef800 nid=0x5b0 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked 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 condition

JNI 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() {
				@Override
				public 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() {
				@Override
				public 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() {
			@Override
			public void run() {
				while (true) // 死循环
					;
			}
		}, "testBusyThread").start();
	}
	public static void createLockThread(final Object lock) {
		new Thread(new Runnable() {
			@Override
			public 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的用法包括: 打印调用堆栈,参数,返回值只是最基本的应用,还可以进行性能监视,定位连接泄漏和内存泄漏,解决多线程竞争问题等;

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值