jvisualvm是官方推荐的最强大的运行监视和故障处理程序,值得java程序员去熟悉掌握它来分析自己的程序。
启动与配置
命令行执行jvisualvm即可启动jvisualvm工具。但是目前默认的插件中心地址已不再可用,需要在 工具->插件->设置 中重新设置插件中心地址。
插件中心地址可在 https://visualvm.github.io/pluginscenters.html 页面中根据使用者安装的jdk版本找到对应的插件地址。
笔者使用的jdk版本时 jdk1.8.0_111,故使用的插件中心地址是 https://visualvm.github.io/archive/uc/8u40/updates.html
Profiler分析程序性能
VisualVM提供了程序运行期间方法级的CPU执行时间分析以及内存分析,做Profiling分析肯定会对程序运行性能有比较大的影响。
package com.gsonkeno.jvmtraining.jconsole;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* 测试jvisualVM的profiler功能
* 分析cpu或内存,但是在“校准”时一直超时,未能真正测试
*/
@RestController
public class JvisualVMController {
@RequestMapping(value = "/jVisualVM")
public Map jvisualVm() throws InterruptedException {
Thread.sleep(Math.round(Math.random()*1000));
Map map = new HashMap();
map.put("name","gs");
map.put("age","25");
return map;
}
}
请求这个restful服务,将会在随机时间内返回(1s内),通过浏览器多次请求访问,获得的CPU分析如下图:
笔者测试时使用的是springboot框架,从图中可以猜测到springboot内嵌的tomcat默认的最小线程数是10,方法执行的平均时间在500ms左右。
笔者测试时,通过写测试用例,并发访问服务,发现线程数也有上升。侧面能够反应tomcat的连接池默认线程数是10,随着并发访问的增大,会逐渐创建新的线程处理用户连接,是线程池使用的一个缩影,这里不再深入说明。
BTrace插件
它的作用是在不停止目标程序运行的前提下,通过HotSpot虚拟机的HotSwap技术动态的加入原本并不存在的调试代码。
- 实际业务代码
package com.gsonkeno.jvmtraining.btrace;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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 br = new BufferedReader(new InputStreamReader(System.in));
for (int i = 0; i < 10; i++) {
br.readLine();
int a = (int) Math.round(Math.random() * 1000);
int b = (int) Math.round(Math.random() * 1000);
System.out.println(test.add(a,b));
}
}
}
- 调试代码
package com.gsonkeno.jvmtraining.btrace;
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
/**
* 调试脚本
*/
@BTrace
public class TracingScript {
@OnMethod(
clazz = "com.gsonkeno.jvmtraining.btrace.BTraceTest",
method = "add",
location = @Location(Kind.RETURN)
)
public static void func(@Self com.gsonkeno.jvmtraining.btrace.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)));
}
// 最终在jvisualvm工具面板Btrace一栏中打印出来的内容,类似如下:
// 调用堆栈:
// com.gsonkeno.jvmtraining.btrace.BTraceTest.add(BTraceTest.java:9)
// com.gsonkeno.jvmtraining.btrace.BTraceTest.main(Unknown Source)
// 方法参数A:754
// 方法参数B:584
// 方法结果:1338
// 调用堆栈:
// com.gsonkeno.jvmtraining.btrace.BTraceTest.add(BTraceTest.java:9)
// com.gsonkeno.jvmtraining.btrace.BTraceTest.main(Unknown Source)
// 方法参数A:106
// 方法参数B:309
// 方法结果:415
}