BTrace 实战案例详解
BTrace 是 Java 动态追踪工具,可以在不重启应用的情况下对运行中的 Java 程序进行安全的热插拔监控。

环境准备
# 下载 BTrace wget https://github.com/btraceio/btrace/releases/download/v2.2.0/btrace-v2.2.0-bin.tar.gz tar -xzf btrace-v2.2.0-bin.tar.gz # 配置环境变量 export BTRACE_HOME=/path/to/btrace-v2.2.0 export PATH=$PATH:$BTRACE_HOME/bin # 查看帮助 btrace -h
基础案例集合
案例 1:方法调用耗时统计
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class MethodTimeTracer {
// 计时用的线程局部变量
@TLS
private static long startTime;
// 监控指定方法
@OnMethod(
clazz = "com.example.service.UserService",
method = "getUserById",
location = @Location(Kind.ENTRY)
)
public static void onEntry(@ProbeMethodName String methodName) {
startTime = timeMillis();
println("=== Enter " + methodName + " ===");
println("Start Time: " + startTime);
}
@OnMethod(
clazz = "com.example.service.UserService",
method = "getUserById",
location = @Location(Kind.RETURN)
)
public static void onReturn(@ProbeMethodName String methodName,
@Duration long duration) {
println("=== Exit " + methodName + " ===");
println("Cost: " + (timeMillis() - startTime) + " ms");
println("Duration: " + duration + " ns");
println("================");
}
}
案例 2:参数和返回值跟踪
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class MethodArgTracer {
@OnMethod(
clazz = "com.example.service.OrderService",
method = "createOrder",
location = @Location(Kind.ENTRY)
)
public static void traceEntry(@Self Object instance,
String orderId,
double amount,
int userId) {
println("=== Create Order ===");
println("Instance: " + instance.getClass().getName());
println("OrderId: " + orderId);
println("Amount: " + amount);
println("UserId: " + userId);
}
@OnMethod(
clazz = "com.example.service.OrderService",
method = "createOrder",
location = @Location(Kind.RETURN)
)
public static void traceReturn(@Return boolean result) {
println("Create Order Result: " + result);
println("===================");
}
// 捕获异常
@OnMethod(
clazz = "com.example.service.OrderService",
method = "createOrder",
location = @Location(Kind.ERROR)
)
public static void traceError(@Thrown Throwable e) {
println("Create Order Error: " + e);
printlnStack();
}
}
案例 3:线程信息监控
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class ThreadMonitor {
// 监控线程启动
@OnMethod(
clazz = "java.lang.Thread",
method = "start"
)
public static void onThreadStart(@Self Thread thread) {
println("=== Thread Started ===");
println("Name: " + thread.getName());
println("Priority: " + thread.getPriority());
println("======================");
}
// 定期打印所有线程状态
@OnTimer(10000) // 每10秒执行一次
public static void printThreadInfo() {
println("=== Thread Dump ===");
Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]);
for (Thread t : threads) {
if (t.isAlive()) {
println("Thread: " + t.getName() +
", State: " + t.getState() +
", Daemon: " + t.isDaemon());
}
}
}
// 监控线程池
@OnMethod(
clazz = "java.util.concurrent.ThreadPoolExecutor",
method = "execute"
)
public static void onTaskSubmit(@Self ThreadPoolExecutor executor) {
println("=== Task Submitted ===");
println("Active Count: " + executor.getActiveCount());
println("Queue Size: " + executor.getQueue().size());
println("Pool Size: " + executor.getPoolSize());
println("Core Pool Size: " + executor.getCorePoolSize());
println("Max Pool Size: " + executor.getMaximumPoolSize());
println("Completed Tasks: " + executor.getCompletedTaskCount());
}
}
高级案例
案例 4:内存和 GC 监控
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class MemoryMonitor {
// 每5秒打印一次内存使用
@OnTimer(5000)
public static void printMemory() {
println("=== Memory Usage ===");
Runtime runtime = Runtime.getRuntime();
long total = runtime.totalMemory();
long free = runtime.freeMemory();
long used = total - free;
long max = runtime.maxMemory();
println("Total: " + formatMemory(total));
println("Used: " + formatMemory(used));
println("Free: " + formatMemory(free));
println("Max: " + formatMemory(max));
println("Usage: " + (used * 100 / max) + "%");
println("====================");
}
private static String formatMemory(long bytes) {
return (bytes / (1024 * 1024)) + " MB";
}
// 监控对象分配
@OnMethod(
clazz = "java.lang.OutOfMemoryError",
method = "<init>"
)
public static void onOOM(@Self OutOfMemoryError e) {
println("!!! OutOfMemoryError !!!");
println(e.getMessage());
printlnStack();
// 触发一次完整的堆转储
jstack();
}
// 监控垃圾回收
@OnMethod(
clazz = "java.lang.System",
method = "gc"
)
public static void onGC() {
println("=== System.gc() called ===");
printMemory();
}
}
案例 5:HTTP 请求监控(Spring Boot)
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class HttpMonitor {
@TLS
private static String requestId;
// 监控 Controller 层
@OnMethod(
clazz = "org.springframework.web.servlet.DispatcherServlet",
method = "doDispatch"
)
public static void onRequest(@Self Object servlet,
@ProbeMethodName String method) {
requestId = Strings.strcat("Req-", String.valueOf(timeMillis()));
println("=== " + requestId + " ===");
println("Request received at: " + new Date());
}
@OnMethod(
clazz = "org.springframework.web.servlet.DispatcherServlet",
method = "doDispatch",
location = @Location(Kind.RETURN)
)
public static void onRequestReturn(@Duration long duration) {
println("Request completed in: " + (duration / 1000000) + " ms");
println("======================");
}
// 监控特定接口
@OnMethod(
clazz = "com.example.controller.UserController",
method = "/api/users"
)
public static void onUserApi(@Self Object controller,
@ProbeMethodName String method) {
println("=== User API Called ===");
println("Controller: " + controller.getClass().getName());
println("Method: " + method);
}
}
案例 6:JDBC 连接监控
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class JDBCMonitor {
private static Map<Object, Object> connectionMap = new HashMap<Object, Object>();
// 监控 Connection 创建
@OnMethod(
clazz = "com.mysql.cj.jdbc.ConnectionImpl",
method = "<init>"
)
public static void onConnectionCreate(@Self Object connection) {
println("=== Connection Created ===");
println("Connection: " + connection);
connectionMap.put(identity(connection), currentTime());
}
// 监控 SQL 执行
@OnMethod(
clazz = "com.mysql.cj.jdbc.StatementImpl",
method = "executeQuery"
)
public static void onQuery(@Self Object statement,
String sql) {
println("=== SQL Query ===");
println("SQL: " + sql);
println("Time: " + currentTime());
// 获取连接信息
if (statement != null) {
println("Statement: " + statement.getClass().getName());
}
}
// 监控连接关闭
@OnMethod(
clazz = "com.mysql.cj.jdbc.ConnectionImpl",
method = "close"
)
public static void onConnectionClose(@Self Object connection) {
println("=== Connection Closed ===");
println("Connection: " + connection);
connectionMap.remove(identity(connection));
println("Active Connections: " + connectionMap.size());
}
// 定期检查活跃连接
@OnTimer(60000)
public static void checkConnections() {
println("=== Active Connections: " + connectionMap.size() + " ===");
if (connectionMap.size() > 10) {
println("WARNING: Too many active connections!");
}
}
}
案例 7:监控特定异常
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class ExceptionMonitor {
// 监控所有异常
@OnMethod(
clazz = "java.lang.Throwable",
method = "<init>"
)
public static void onException(@Self Throwable t) {
String className = t.getClass().getName();
// 过滤特定异常
if (className.contains("BusinessException") ||
className.contains("SystemException")) {
println("=== Exception Created ===");
println("Type: " + className);
println("Message: " + t.getMessage());
// 直接打印堆栈
jstack();
// 简单的计数器
incrementAndPrint("ExceptionCount");
}
}
@Property
private static long exceptionCount;
private static void incrementAndPrint(String name) {
exceptionCount++;
println(name + ": " + exceptionCount);
}
// 监控异常抛出点
@OnMethod(
clazz = "/com\\.example\\..*/",
method = "/.*/"
)
public static void traceMethod(@ProbeClassName String className,
@ProbeMethodName String methodName,
@Self Object instance) {
if (instance instanceof Throwable) {
println("Throwing in: " + className + "." + methodName);
}
}
}
实用脚本模式
案例 8:性能瓶颈分析
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class PerformanceAnalyzer {
@TLS
private static Map<String, Long> methodTimes = new HashMap<String, Long>();
@TLS
private static Map<String, Integer> methodCounts = new HashMap<String, Integer>();
@OnMethod(
clazz = "/com\\.example\\..*/",
method = "/.*/"
)
public static void traceMethod(@ProbeClassName String className,
@ProbeMethodName String methodName) {
String key = className + "." + methodName;
methodCounts.put(key,
methodCounts.containsKey(key) ?
methodCounts.get(key) + 1 : 1);
if (methodCounts.get(key) % 100 == 0) {
println("Method " + key + " called " +
methodCounts.get(key) + " times");
}
}
// 定期输出热点方法
@OnTimer(60000)
public static void printHotspots() {
println("=== Method Call Counts ===");
for (Map.Entry<String, Integer> entry : methodCounts.entrySet()) {
if (entry.getValue() > 100) {
println(entry.getKey() + ": " + entry.getValue() + " calls);
}
}
}
}
案例 9:线上问题定位
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class ProblemDiagnoser {
// 监控死锁
@OnTimer(30000)
public static void detectDeadlock() {
println("=== Deadlock Detection ===");
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
if (deadlockedThreads != null) {
println("!!! DEADLOCK DETECTED !!!");
ThreadInfo[] threadInfo = threadMXBean.getThreadInfo(deadlockedThreads);
for (ThreadInfo info : threadInfo) {
println("Thread: " + info.getThreadName());
println("State: " + info.getThreadState());
println("Lock: " + info.getLockName());
println("==================================");
}
}
}
// 监控 CPU 使用率高的线程
@OnTimer(10000)
public static void monitorCPU() {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadMXBean.getAllThreadIds();
for (long id : threadIds) {
long cpuTime = threadMXBean.getThreadCpuTime(id);
if (cpuTime > 1000000000) { // CPU时间超过1秒
ThreadInfo threadInfo = threadMXBean.getThreadInfo(id);
println("High CPU Thread: " + threadInfo.getThreadName());
println("CPU Time: " + (cpuTime / 1000000000) + "s");
printlnThreadStack(threadInfo);
}
}
}
}
执行案例
# 查找进程 ID jps -l # 执行 BTrace 脚本(阻塞模式) btrace <pid> MethodTimeTracer.java # 后台执行 btrace -p <port> <pid> MethodTimeTracer.java & # 使用退出命令 btrace -o /tmp/result.txt <pid> MethodTimeTracer.java # 带参数的执行 btrace -d <classpath> <pid> MethodTimeTracer.java
注意事项
# 1. 生产环境安全模式(只读操作) btrace -c <pid> MethodTimeTracer.java # 2. 限制输出 btrace -w <pid> MethodTimeTracer.java # 只输出到当前窗口 btrace -s <pid> MethodTimeTracer.java # 同步模式 # 3. 优雅退出 jcmd <pid> BTrace.stop
这些案例覆盖了 BTrace 的主要应用场景,实际使用时需要根据业务场景调整类名、方法名和监控逻辑,在使用生产环境时,建议先在测试环境验证脚本的正确性和安全性。