BTrace 无法监控 new 对象,因其仅支持方法级观测且禁止创建对象、使用循环等;Java Agent 配合 Byte Buddy 可在构造器入口织入代码获取调用栈,实现源头追踪。

可以直接监控可疑对象的创建位置,但方式和限制完全不同:BTrace 无法追踪对象实例化(new 行为),而 Java Agent(配合 Byte Buddy 或 ASM)可以安全实现。
为什么 BTrace 不能监控 new 对象
BTrace 的设计目标是方法级观测,它只支持在已有方法的入口、出口、异常点等位置插入逻辑。JVM 字节码中 new 指令本身不对应 Java 方法调用,也没有可标注的“方法签名”。BTrace 脚本受限于以下硬性规则:
- 不允许创建新对象(包括 new Person()、new ArrayList() 等)
- 不能使用数组、循环、synchronized、try-catch
- 所有方法必须是 static,且只能调用 BTraceUtils 或自定义静态工具方法
- 无法拦截字节码层级的 new、dup、invokespecial
序列
因此,想用 BTrace 脚本“打印出 Person 是在哪一行 new 出来的”,技术上不可行,不是配置问题,而是模型限制。
Java Agent + Byte Buddy 实现 new 源头追踪
真正可行的方式是使用 Java Agent,在类加载或重转换阶段,对目标类(如 Person)的构造器(
- 用 Byte Buddy 的 Advice 在
方法入口织入代码,避免修改方法签名 - 在 Advice 中调用
Thread.currentThread().getStackTrace()获取创建时的调用链 - 注意:该堆栈需在构造器执行早期获取,否则可能被优化或截断;建议放在
@Advice.OnMethodEnter最开头 - 上报逻辑必须轻量——只记录类名、行号、线程ID,避免 I/O 或远程调用阻塞业务线程
- 目标类(Person)若已加载,需用
Instrumentation.retransformClasses(),而非 redefine
实操建议:聚焦高价值目标,避免全量扫描
盲目监控所有 new 操作开销大、日志爆炸。更务实的做法是:
- 先通过内存分析(如 jmap + MAT)确认哪个类实例暴涨,锁定目标(如 com.example.UserSession)
- 仅对该类的
增强,加采样率(例如每 100 次记录 1 次) - 在 Advice 中打印:类名、调用类、调用方法、源码行号、时间戳
- 将结果异步写入本地 ring buffer 或通过 JMX 暴露,避免影响吞吐
替代方案:用 JVM 自带的 -XX:+TraceClassLoading 不够?
-XX:+TraceClassLoading 只告诉你哪个类被加载,不反映对象创建位置。-XX:+PrintGCDetails 也只体现 GC 行为。它们无法回答“谁在什么时候 new 了这个对象”。真正需要的是构造器调用上下文,这必须靠字节码增强实现。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











