1.Visualvm兼容范围与插件安装
Visualvm基于NetBeans平台开发工具,具备通过插件扩展功能的能力。有了插件扩展功能,Visualvm可以做到:
- 显示虚拟机进程以及进程的的配置,环境信息(jps,jinfo)
- 监视应用存储的处理器,垃圾收集,堆,方法区以及线程的信息(jstat,jstack)
- dump及分析堆转储快照(jmap,jhat)
- 方法级的程序运行性能分析,找出被调用最多,运行时间最长的方法
- 离线程序快照:收集程序的运行时配置,线程dump,内存dump等信息建立一个快照,可以将快照发送给开发者进行Bug反馈
2.生成,浏览堆转储快照
生成堆转储快照有两种方式:
- 在“应用程序”窗口中右键应用程序节点,选择“堆Dump"
- 在“应用程序”窗口中双击应用程序节点,打开应用程序标签,然后在”监视“标签中单击”堆Dump“
[heapdump]文件是临时文件,需要手动保存。手动保存的dump文件可以通过 文件 -> 装入 功能打开
3.程序性能分析
在Profiler页签中,Visualvm提供了程序运行期间方法级的处理器执行时间分析和内存分析。做Profiling分析对程序运行性能有较大影响,所以一般不会在生产环境使用该功能。
4.BTrace动态日志追踪
Btrace的作用是在不中断目标程序运行的前提下,通过HotSpot的Instrument功能动态添加原本不存在的调试代码。
package com.ryj.hotspot; import java.util.ArrayList; import java.util.List; import com.ryj.java8.lambda.apple.Apple; public class JavaHeapTest { static List<Apple> appleList = new ArrayList<>(); public static void main(String[] args) { verificationOOM(); } private static void verificationOOM() { Integer i = 0; while (true) { addApple(++i); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private static Apple addApple(Integer a) { System.out.println(a); Apple apple = new Apple("red", 12.5f); appleList.add(apple); return apple; } }
/* BTrace Script Template */ import com.sun.btrace.annotations.*; import com.alibaba.fastjson.JSON; import static com.sun.btrace.BTraceUtils.*; @BTrace(unsafe = true) public class TracingScript { /* put your code here */ @TLS private static long startTime = 0; @OnMethod(clazz="com.ryj.hotspot.JavaHeapTest",method="addApple") public static void startExecute(){ startTime = timeNanos(); //println(strcat("开始时间: ",str(startTime))); } @OnMethod(clazz="com.ryj.hotspot.JavaHeapTest",method="addApple",location=@Location(Kind.RETURN)) public static void endExecute(@Self com.ryj.hotspot.JavaHeapTest instance, Integer a, @Return com.ryj.java8.lambda.apple.Apple apple){ long endTime = timeNanos(); //println(strcat("结束时间: ",str(endTime))); println(strcat("方法参数A: ",str(a))); println(strcat("execute time(nanos): ", str(endTime-endTime))); //println(strcat("result: ", str(apple.toString()))); println(strcat("result: ", JSON.toJSONString(apple))); } }
Btrace之所以安全可靠的,是因为它对正在运行的程序是只读的。也就是说,他可以插入跟踪语句来检测和分析运行中的程序,不允许对其进行修改。因此他存在一些限制:
- 不能创建对象
- 不能创建数组
- 不能抛出和捕获异常
- 不能调用任何对象方法和静态方法
- 不能给目标程序中的类静态属性和对象的属性进行赋值
- 不能有外部、内部和嵌套类
- 不能有同步块和同步方法
- 不能有循环(for, while, do..while)
- 不能继承任何的类
- 不能实现接口
- 不能包含assert断言语句
这些限制其实是可以使用unsafe模式绕过,通过声明 *@BTrace(unsafe = true)
annotation。实际使用非安全模式跟踪时,发现一个问题,一个进程如果被安全模式btrace探测过一次, 后面再使用非安全模式进行探测时非安全模式不生效。
- @BTrace 声明了这个类是BTrace脚本。unsafe参数表示是否不安全的模式执行.
- @OnMethod 声明了关注点,必须声明在公有的静态方法上public static void。静态方法为 在关注点上执行的跟踪动作
- @TLS声明的变量是 ThreadLocal的, 每个线程都会有一份这个自己的startTime 变量
- location: 用@Location来表明在什么时候时候去执行脚本
- @Self 声明探测的当前对象this
- @Return 方法返回对象
基本实现逻辑:虚拟机其实提供了一个hook,那就是Instrumentation,可以将独立于应用程序的代理程序agent程序随着着应用程序一起启动或者attach挂载到正在运行中的应用程序。而在代理程序中可以对class进行修改或者重新定义。