埋点与插桩

埋点与插桩

非侵入式埋点

运行时代码诊断,首先需要考虑的就是如何将埋点代码嵌入至业务代码中;常见做法主要有四种:侵入式埋点、运行时增强、编译时增强、中间件增强,后三种都属于非侵入式埋点,埋点性能逐步提升。

  • 侵入式埋点:每个业务方都需要主动感知或唤起埋点代码,仅适用于小范围内部产品,不宜大范围推广。

  • 运行时增强:程序运行时动态拦截,如 Spring AOP、jvm-sandbox。优点是动态可插拔,控制灵活;缺点是有运行时额外开销。

  • 编译时增强:程序编译时进行增强,如 AspectJ、ASM。优点是几乎没有运行时额外开销;缺点是需要额外的构建步骤,启动比较慢。

  • 中间件增强:在各个中间件服务中嵌入埋点代码。优点是原生代码,没有任何额外开销;缺点是需使用统一的中间件服务。

调用拦截

调用拦截的目的是为了明确线程监听的对象与生命周期。传统的线程诊断,由于没有明确的监听对象与生命周期,只能进行全局线程的随机监听,不但浪费了大量的系统资源,还经常遗漏关键调用的线程信息。

性能诊断

性能诊断主要有插桩(Instrumentation)和采样(Sampling)两种实现方式,前者开销较高,后者结果有一定误差。

  • 插桩法(Instrumentation),需要在每个方法前后添加埋点,计算耗时,优点是结果准确,缺点是开销较高,需要明确目标函数,代码结构也会发生一定变化,很多在线诊断工具,如 Arthas 都是采用的这种方法。

  • 采样法(Sampling),则是周期性获取 JVM 运行栈快照,然后通过比对方法在快照集(Snapshot Set)中出现的次数来粗略估计其运行时间。每次快照生成均需等待 JVM 运行到安全点(Safe Point),因此,运行结果有一定误差,不过,性能开销要远低于插桩法,而且能够获取完整线程栈,无需事先指定目标函数。主流的 Java Profiler 都是采用的 Sampling 方式。

image