栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

为什么不用nativeagent

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么不用nativeagent

如果常看jvmti的文档,我们会发现jvm提供了两种agent。一种是javaagent,一种是native agent。
世面上的大多数apm程序为什么都是用javaagent呢。

对比两个agent

java agent可以说上手非常方便,都是java的代码,和平时编写的java代码可以说没区别。

    public static void premain(final String args, Instrumentation inst) {
    }

    public static void agentmain(final String args, Instrumentation inst) {
    }

这是agent的入口函数,premain是在-javaagent启动的时候,main方法执行之前执行。agentmain则是通过动态load触发的入口。下面看一段简单的案例。

public class Main {

    public static void premain(final String args, Instrumentation inst) {
 agentmain(args, inst);
    }

    public static void agentmain(final String args, Instrumentation inst) {

 inst.addTransformer(new ClassFileTransformer() {

     public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined,
 ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

  className = className.replace("/", ".");
  if (!className.contains("$") && (isSpecialClass(className) || isReTransformClass(className, args))) {

      ClassReader cr;
      cr = new ClassReader(classfileBuffer);
      ClassNode cn = new ClassNode();
      cr.accept(cn, 0);
      if (SYSTEM.contains(className)) {
   SystemTransform at = new SystemTransform();
   at.trans(cn);
      } else {
   CustomTransform at = new CustomTransform();
   at.trans(cn);
      }
      ClassWriter cw = new ClassWriter(0);
      cn.accept(cw);
      byte[] toByte = cw.toByteArray();
      return toByte;


  }

  return null;
     }
 }, true);

 Class[] allLoadedClasses = inst.getAllLoadedClasses();
 List retransformClasses = new ArrayList(allLoadedClasses.length);
 for (Class clazz : allLoadedClasses) {
     String name = clazz.getName();
     if (isSpecialClass(name) || isReTransformClass(name, args)) {
  if (inst.isModifiableClass(clazz) && !name.contains("$")) {
      retransformClasses.add(clazz);
  }
     }
 }
 try {
     if(retransformClasses.size()>0){
  inst.retransformClasses(retransformClasses.toArray(new Class[0]));
     }
 } catch (UnmodifiableClassException e) {
     e.printStackTrace();
 }
    }

    public static boolean isSpecialClass(String className) {
 return SYSTEM.contains(className);
    }

    public static boolean isReTransformClass(String className, String args) {
 if (NOCLASS.equals(args)) {
     return false;
 }

 if (className.contains(args)) {
     return true;
 }

 return false;
    }

}

中间有一段通过asm做字节码注入的逻辑。了解更多请查看

https://github.com/xpbob/lightTrace

javaagent可以无缝的往一个java进程上执行一段java代码,并且可以获取到字节码进行类的转化。

native agent的入口如下

JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {}
JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm) {}

看到了c++代码,瞬间研发难度增加。
上面是加载和卸载时候调用的方法。
native agent大部分操作都是基于jvmti的事件,基于事件写回调,这个是字节码注入做不到的。例如像查看所有的异常,javaagent就需要把所有的类的方法都改造一下,都做try catch。如果类特别多的话,耗费资源的大户。jvmti则是简单注入一下事件,加一个回调函数就可以。

JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
    jvmtiEnv *jvmti = NULL;
    jint ret = vm->GetEnv(reinterpret_cast(&jvmti), JVMTI_VERSION_1_2);

    if (ret != JNI_OK) {
 fprintf(stderr, "ERROR: Couldn't get JVMTI environment");
 return JNI_ERR;
    }
    // capbilities
    jvmtiCapabilities caps;
    std::memset(&caps, 0, sizeof(caps));
    caps.can_generate_monitor_events = 1;
    caps.can_generate_exception_events = 1;

    auto err = jvmti->AddCapabilities(&caps);
    if (err != JVMTI_ERROR_NONE) {
 fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI");
 return JNI_ERR;
    }
    // callback
    jvmtiEventCallbacks cb;
    cb.Exception = &jvmti_EventException;
    jvmti->SetEventCallbacks(&cb, sizeof(cb));
    jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, NULL);
    return JNI_OK;
}

开启注册开关,然后设置好回调函数。

void JNICALL jvmti_EventException
 (jvmtiEnv *jvmti_env,
  JNIEnv
  *jni_env,
  jthread thread,
  jmethodID
  method,
  jlocation location,
  jobject
  exception,
  jmethodID catch_method,
  jlocation
  catch_location) {}

回调函数是固定的参数,基本足够我们使用了,只要进行收集就可以做到。
native agent也可以做字节码注入的这些事情,但是还得编写java代码来做。

怎么看native agent更加强大。为什么还是用javaagent的多。

业务分析

1.编写难度不一样
javaagent编写容易,上手简单,只要是java程序员问题不大。native agent则需要c/c++编写,语法难度大,而且容易造成crash。
2. 业务需求大多数在java代码上
我们常见的监控,需要jvm的信息,也同时需要业务的信息。例如我们想做调用链追踪。这个已经是业务代码层的问题,而不是jvm层面的问题,需要用字节码改造或者替换jar包的方式进行指标的暴露。和业务相关的部分,nativeagent则是无能为力。

native的部分优势

我们常见的问题都是业务问题,那是不是没有native agent的使用呢,其实还是有的,例如性能调优,我们想做一些cpu和线程的信息采集,或者是异常的汇总,锁的时间分析。这些都是native agent才能做到,可以说更贴近jvm的事情都是需要native agent来做的。只不过这种场景对比监控来说,确实少之又少。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号