暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

JVM监控之java agent和native agent

China中间件 2017-06-02
2709

引言

近年来在中间件服务器上的APM产品广泛应用,它可以实现 系统性能问题的底层定位,实现业务请求的细颗粒度监控,能迅速帮运维人员定位到性能问题的代码点。APM产品众多,其实原理都是从JVM层面入口,对其上层应用的和中间件的运行情况进行获取快照,然后进行数据分析和跟踪。如下图:

那这些产品都一样的调用JVM的底层agent接口吗?其实不一样的,这里有两种agent方式,一种是java agent,另一种是native agent.本文就是简单介绍一下这两种方式的原理和区别。我们知道那种好,哪种不好就可以,这里比较复杂,除非你是做APM产品的,那么对深层的结构就要了解非常清楚才可以。

 

当我们上线一套新的应用系统的时候,可能会花很多时间去思考该用什么样的方法可以对生产环境的应用代码进行监控,操作和更新。对于JVM了解比较深入的开发者会想到使用一个新的工具,那就是非常强大的JVM外部的接口JVMTI,它是和JVM集成在一起的统一接口,这个就是javaagent。

如果我们的agent是调用OS本地或者java的库,而且这些JVM提供的功能是不能被普通的应用程序代码调用到的。像一些监控JVM的诊断工具,就是要在JVM启动的时候加agent库文件来获取JVM的性能数据,而像Takipi这样的工具就是利用JVM的基础功能給本地带提供性能数据。其实这就是native agent。


上面提到了两种agent,java agent和nativeagent.这两种都是要在JVM启动的时候进行加载,但是这两种方式完全不同的模式。


Java agent


Java agent就是我们通常看到的.jar文件,这个文件中定义了一个标准premain()方法,(我们应用的入口主方法都是main()方法)这些方法是被JVM优先于应用代码来加载的。

说到java agent必须要讲的是一个叫做instrument(官方链接:http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.html)的JVMTIAgent(linux下对应的动态库是libinstrument.so),因为就是它来实现javaagent的功能的。

这个模块作为JVM启动的时候一个参数传进去。通过持有这个对象,agent的代码(它的行为与root类加载器加载的任何Java代码一样)可以执行一些非常强大的功能。

 

publicstatic void premain(String agentArgs, Instrumentation inst) {

myInst =inst; grab a reference to the inst object for use later

}

例子类:

public class MyAgent {

 

    /**

     * 该方法在main方法之前运行,与main方法运行在同一个JVM中

     * 并被同一个System ClassLoader装载

     * 被统一的安全策略(security policy)和上下文(context)管理

     *

     */

    publicstatic void premain(String agentOps, Instrumentation inst) {

       System.out.println("=========premain方法执行========");

       System.out.println(agentOps);

    }

 

    /**

     * 如果不存在 premain(String agentOps,Instrumentation inst)

     * 则会执行 premain(String agentOps)

     */

    publicstatic void premain(String agentOps) {

       System.out.println("=========premain方法执行2========");

       System.out.println(agentOps);

    }

 

这个功能是1.5版本以后引入的。部分主要功能:

  • 加载应用java类之前优先加载,同时修改字节码;

其中一个强大的功能就是可以动态的对正在运行的目标代码类方法内容进行重写,而且是不影响方法的字段结构。这样就实现了agent本质上对正在运行的字节码格式的类方法内容进行了重写。比如,可以在目标执行类方法的开始增加starttime,执行结束增加endtime,或者记录其运行的参数值,实现了时间性能的监控。另一个用处是可以实现JVM不重启的条件下实现类的reload。

  • 获取某个对象的大小;

  • 获取已经被加载的类;

  • 获取已经被初始化了的类;

 

缺点:Jva Agent是在JVM 启动后期才被加载,该Agent是运行在JVM运行时的容器里,它会受到JVM 挂起或 Java 运行时问题的影响,并且不能报告某些类型的错误信息。如果不能正常发送监控数据就要堆积在JVM操作里,影响应用系统性能。

优点:简单,比较好实现,可移植性强,利用了JAVA的平台无关性。

Native agents

Native agents的机制是完全不同于java agent.如果说java agent是已经非常强大可以做很多酷的事情,那么Native agents则是更胜一筹的另一个level,因为它并不是用java写的agent.它是不受常规java代码标准现在限制的更加灵活的代码,比如c++.不仅仅是这个,它的JVMTI(JVM Tooling Interface)接口提供了一系列及其强大的功能。

JVM在启动的时候会动态加载C++的库文件,这些库文件中的jvmti.h文件会暴露出所有这一系列的API.通过这些API可以实时获取JVM中正在running的类相关属性。包括内存块分区,包括gc,locking,代码操作,synchronization, thread management, compilation debugging等等。

void  JNICALL ExceptionCallback(jvmtiEnv *jvmti,

JNIEnv  *jni, jthread thread, jmethodID method,

jlocation  location, jobject exception,

jmethodID  catch_method, jlocation catch_location)

 

优点:

  • 由于native agent 并不作为JVM 运行时的一部分,它不会受到JVM 停止的影响(例如:较长的垃圾收集挂起、运行时错误等),因此能够一直向外部工具传输数据。

  • Native Agent能够在任何类加载之前加载。这使NativeAgent能够从一开始就采集数据,并可使其不会受到任何约束的,对所有的 Java 代码进行数据采集和控制执行。

  • 通过JVM native接口,可以获得更为详尽的性能信息,例如操作系统的高精度时钟、详细的垃圾收集数据等。

  • 在Native Agent内部,可以采集到与JVM 有关的大量信息,例如内存、线程、JVM 崩溃等。尤其对于线程和内存分析,访问JVM 线程和内存使用情况以及本地线程和内存使用情况有助于性能监测。一旦出现内存不足错误导致崩溃,因为本地进程仍在运行,所以Native Agent仍能采集堆栈中的数据和内存信息。

缺点:

复杂度高:相对于java agent API,native agent 只提供了一小部分可以定制的模块调用,而且编写起来非常复杂。

可移植性差:native agent编写的都是.so或.dll的库文件在平台依赖性比较高,所以可一致性差,不像java的agent.

稳定性方面:native在底层运行的出现错误可能会直接导致JVM 退出。

 

总结

通过上面的介绍两种agent的原理不同,各有利弊,可以在实际的环境中根据需求和能力来确定使用哪种。

希望本文有助于理解这两种类型agnet之间的差异。知道什么是agent,以及它们是如何构建的,因为即使我们从来后者以后没有写过agent,但是作为java中间件的运维人员 ,你可能会依赖其中一个或多个来为你的应用程序提供性能监控能力。


欢迎长按扫描关注 【China中间件】公众号,本平台专注中间件技术的分享和传播,有问题敬请留言。


文章转载自China中间件,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论