不能用classfiletransformer实现全自动耗时埋点,因其受jvm重定义限制(不可增删方法/字段、不可改签名),android非debuggable进程禁用且art不支持,首次加载类无法拦截,性能开销大。

不能在类加载期直接用 ASM 动态修改字节码实现全自动耗时埋点——ClassFileTransformer 虽然支持运行时字节码修改,但会触发 JVM 的类重定义限制(如不能新增/删除方法、不能改签名),且对 java.* 和启动类加载器加载的类无效,实际埋点几乎必然失败。
为什么不能靠 ClassFileTransformer 做全自动耗时埋点
很多人一上来就查 Instrumentation.addTransformer(),以为加个 ClassFileTransformer 就能无感插桩。现实是:
- JVM 对
retransformClasses()有严格约束:只能修改已有方法体(Code属性),不能增删字段、方法,也不能改方法签名或异常表 —— 而耗时统计需要插入System.nanoTime()开始/结束调用,还常依赖静态计数器或 Map 缓存,这些都得提前准备好字段或辅助方法 - Android 上根本不可用:
Instrumentation在非 debuggable 进程里被禁用,且 ART 不支持运行时 retransform(从 Android 8.0 起彻底移除) - 即使在桌面 JVM,首次加载类时无法拦截(
transform()只对后续defineClass或retransform生效),导致 Application 类、Activity 生命周期等关键入口漏埋 - 性能开销不可控:每个类加载都走 transformer,大量反射调用 + 字节码解析,冷启动拖慢 20%+ 很常见
真正可行的路径:编译期插桩 + Transform Action(AGP 7.0+)
Android 官方明确废弃了旧版 Transform API,现在必须用 TransformAction 配合 AsmClassVisitorFactory。这是目前唯一稳定、可控、能覆盖全量方法的方案:
-
AsmClassVisitorFactory是 AGP 封装的轻量入口,它自动帮你处理 class 文件读写、多线程安全、增量编译跳过逻辑,不用手动写ClassReader/ClassWriter - 你只需继承
AsmClassVisitorFactory,重写createClassVisitor(),返回一个自定义ClassVisitor,在里面用visitMethod()拦截目标方法 - 耗时逻辑建议封装成独立工具类(如
TimingMonitor.recordStart(String)/recordEnd(String)),避免在每个方法里重复生成时间戳指令;ASM 插入时只调这两句即可 - 注意过滤系统类和第三方 SDK:通过
className.startsWith("android.")或白名单机制跳过,否则可能破坏OkHttp、Retrofit等库的字节码结构
ASM 插入耗时代码时最容易踩的坑
手写 MethodVisitor 插入前后计时,看着简单,实际极易出错:
- 别硬编码局部变量索引:方法参数数量、是否为
static、是否含long/double(占两个 slot)都会影响 index,必须用AdviceAdapter自动管理栈和变量槽位 - 不要在
visitCode()后直接visitInsn(Opcodes.LRETURN)这类指令:需先调用visitVarInsn(Opcodes.LSTORE, slot)保存起始时间,再在方法末尾visitVarInsn(Opcodes.LLOAD, slot)读取,否则栈不平衡导致 VerifyError - Android 上慎用
System.nanoTime():部分低端机型该方法耗时高达 1~2μs,反而污染统计结果;可改用SystemClock.uptimeMillis()(精度 10~15ms,但更轻量) - 如果目标方法本身抛异常,你的
recordEnd()必须放在finally块里 —— 这意味着要插入TRYCATCHBLOCK,不是简单追加几行指令,得用LocalVariablesSorter+TraceMethodVisitor辅助分析控制流
最麻烦的不是插桩本身,而是如何让耗时数据能被业务层消费:你得在插桩时动态注入唯一 methodId(比如 className + "." + methodName + descriptor 的 hash),再确保这个 ID 能和后台埋点 schema 对齐。漏掉这步,所有字节码都插对了,数据也传上去了,但查不到对应方法 —— 这种问题线上根本没法 debug。











