原文:http://apmblog.dynatrace.com/2014/01/15/pros-and-cons-of-using-java-vs-native-agent-for-application-performance-management/
许多应用性能管理(APM)厂商在对 JVM运行时性能进行深入分析时,一般使用JVM运行时的接口,而JVM运行时提供两种接口:JVMPT和JVMTI。JVMPI 接口从 Java 5后逐渐被JVMTI 所取代,且JVMTI接口都允许APM厂商将本地库(通常称作Native Agent)加载到与JVM 同一个进程中,使得该本地库可以通过本地的 API (C/C++代码)访问JVM 运行时的状态和应用程序的性能数据。另外,由于该库并不作为JVM 运行时的一部分,它不会受到JVM 停止的影响(例如:较长的垃圾收集挂起、运行时错误等),因此能够一直向外部工具传输数据。
Java 5 还引入了一种纯 Java 接口方案(通常称作Java Agent),作为这种本地接口的替代方案。这种方案允许将Java Agent加载到JVM 之中,并作为JVM 的一部分运行。其“不利”的一面是,Jva Agent是在JVM 启动后期才被加载,该Agent是运行在JVM运行时的容器里,它会受到JVM 挂起或 Java 运行时问题的影响,并且不能报告某些类型的错误信息。
在本博客中我们将主要阐述,为何Dynatrace的工程团队决定采用Native Agent方式结合字节码插码技术(bytecode instrumentation,BCI)的方式,而不是采用基于 Java Agent的方式,来监控应用程序的性能。
Native Agent能够在任何类加载之前加载。这使Native Agent能够从一开始就采集数据,并可使其不会受到任何约束的,对所有的 Java 代码进行数据采集和控制执行。为能捕获方法级的信息,可利用字节码插码技术(BCI)实现优势互补,而不是依赖于Native接口的回调。这样不仅能对任何类执行字节码插码,还能对核心系统类进行深入分析(java.lang.Object、java.lang.Thread等)。
通过JVM native接口,我们可以获得更为详尽的性能信息,例如操作系统的高精度时钟、详细的垃圾收集数据等。由于采用了Native Agent方式,因此不必另外安装用于采集系统信息的程序。而Java Agent很可能无法访问上述数据,因为它运行于JVM 内的特殊的安全背景环境之中。
在Native Agent内部,我们可以采集到与JVM 有关的大量信息,例如内存、线程、JVM 崩溃等。尤其对于线程和内存分析,访问JVM 线程和内存使用情况以及本地线程和内存使用情况有助于性能监测。一旦出现内存不足错误导致崩溃,因为本地进程仍在运行,所以Native Agent仍能采集堆栈中的数据和内存信息。
【线程死锁分析】
【内存泄露分析】
通过本地代码调用JVMTI接口,可以更加有效的获取性能分析的数据,例如,通过线程快照(Thread Snapshot)的方式获取线程栈信息,而从JVM 内部的Java Agent调用JVMTI接口获取此类信息所需的代价要高很多,然而,对线程栈做快照恰恰是定位Java方法执行缓慢的最佳途径,由于对JVM的负载太高,以至于部分Java Agent的APM厂商不得不放弃此功能来达到用户对性能开销的要求。
由于Native Agent没有挂靠(attach)在 JVM 上,因此不会受到 JVM 挂起(特别是与垃圾收集)的影响,可以在JVM 停止(但进程仍然运行)期间仍采集数据,这样可以帮助我们采集有关垃圾回收所导致的JVM挂起对当前正在执行应用线程的实际影响的信息,而 Java Agent则无法获得这样的信息,因为它也会受到JVM挂起的影响而暂停收集数据。
Native Agent因为工作原理的不同,导致其与Java Agent相比,拥有明显的优势,具体总结如下:
1. 获取JVM运行时的性能参数。
2. 获取JVM线程方法调用栈信息
3. 不受JVM的运行状态影响。
4. 开销更少