添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在上篇博客(http://blog.csdn.net/raintungli/article/details/51646556)中提到了在on_attach的方式上如何重新定义class,里面也提到了最后attach时候会调用我们自定义的agent class的agentmain方法,在Instrumentation的接口里面实际上本身提供了redfineClasses的方法

也就是agentmain的方法只是一个调用入口,还是需要调用sun本身提供的Instrumentation 的redfineClasses的方法去替换classes

	public static void agentmain(String agentArgs, Instrumentation inst) {
		ClassDefinition def1 = new ClassDefinition(Class, classByte);
		inst.redefineClasses(new ClassDefinition[]{def1});
	}
在sun.instrument.InstrumentationImpl 中的redefineClasses 还是调用native redefineClasses0的方法
private native void
    redefineClasses0(long nativeAgent, ClassDefinition[]  definitions)
        throws  ClassNotFoundException;
JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_redefineClasses0
  (JNIEnv * jnienv, jobject implThis, jlong agent, jobjectArray classDefinitions) {
redefineClasses(jnienv, (JPLISAgent*)(intptr_t)agent, classDefinitions);
}
下面是redefineClasses的部分代码
void redefineClasses(JNIEnv * jnienv, JPLISAgent * agent, jobjectArray classDefinitions) {
    if (!errorOccurred) {
        getDefinitionClassMethodID = (*jnienv)->GetMethodID(    jnienv,
                                                classDefClass,
                                                "getDefinitionClass",
                                                "()Ljava/lang/Class;");
     ......
    if (!errorOccurred) {
        getDefinitionClassFileMethodID = (*jnienv)->GetMethodID(    jnienv,
                                                    classDefClass,
                                                    "getDefinitionClassFile",
                                                    "()[B");
        .....
          classDefs[i].klass = (*jnienv)->CallObjectMethod(jnienv, classDef, getDefinitionClassMethodID);
          targetFiles[i] = (*jnienv)->CallObjectMethod(jnienv, classDef, getDefinitionClassFileMethodID);
           classDefs[i].class_byte_count = (*jnienv)->GetArrayLength(jnienv, targetFiles[i]);
           if (!errorOccurred) {
                    jvmtiError  errorCode = JVMTI_ERROR_NONE;
                    errorCode = (*jvmtienv)->RedefineClasses(jvmtienv, numDefs, classDefs);
		.....

获取调用传递过来的ClassDefinition 里的class对象和需要修改的字节码,最后调用了JVMTI 的RedefineClasses的方法
JvmtiEnv::RedefineClasses(jint class_count, const jvmtiClassDefinition* class_definitions) {
//TODO: add locking
  VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_redefine);
  VMThread::execute(&op);
  return (op.check_error());
} /* end RedefineClasses */

后是交给VM Thread去执行的,为什么要交给VM Thread,修改Class内容需要进入Safepoint的点的是stop-world的操作,而VM Thread的操作都会进入Safepoint的点

RedefineClasses 分为3部分,参考jvmtiRedefineClasses.cpp

第一是doit_prologue 这个被在JavaThread里调用主要是解析和校验传递过来的bytecode生成scrash_class
  • 挨个遍历要批量重定义的jvmtiClassDefinition
  • 然后读取新的字节码,如果有关注ClassFileLoadHook事件的,还会走对应的transform来对新的字节码再做修改
  • 字节码解析好,创建一个klassOop对象
  •  对比新老类,并要求如下:
  1. 父类是同一个
  2. 实现的接口数也要相同,并且是相同的接口
  3. 类访问符必须一致
  4. 字段数和字段名要一致
  5. 新增的方法必须是private static/final的
  6. 可以删除修改方法
  7. 对新类做字节码校验
第二是doit() 这是在VMThread里执行的主要是替换原来的class里的内容
  • 合并常量
  • 清除原来类上的断点
  • 清除原来类上的JIT优化
  • 将老的jmethodId更新到新类的jmethodid上
  • 将新类的常量池的hodler指向老的类
  • 将新类和老类的一些属性做交换,比如常量池,methods,内部类
  • 初始化新的vtable和itable
  • 交换annotation的method、field、parameter
  • 遍历所有当前类的子类,修改他们的vtable及itable
第三是doit_epilogue() 
  • ·主要是释放前面的步骤内存
在上篇博客(http://blog.csdn.net/raintungli/article/details/51646556)中提到了在on_attach的方式上如何重新定义class,里面也提到了最后attach时候会调用我们自定义的agent class的agentmain方法,在看Instrumentation的接口,里面本身提供了redfineClasses的方法也就是agentmai
java.lang.Instrument包是在 JDK5引入的,程序员通过修改方法的字节码实现动态修改类代码 。这通常是在 类的main方法调用之前 进行预处理的操作,通过java指定该类的代理类来实现。在 类的字节码载入JVM前 会调用ClassFileTransformer的transform方法,从而实现修改原类方法的功能,实现AOP,这个的好处是 不会像动态代理或者CGLIB技术实现AOP那样会产生一个新类,也不需要原类要有接口 。   (1) 代理 (agent) 是在你的main方法前的一个拦截器 (interceptor),也是在main方法执行之前,执行agent的代码。 a
@Since:1.5Comment:该类作为方法的参数块。 用来绑定需要用新的类文件字节定义ClassFileTransformer @Since:1.5Comment:代理提供了该接口的实现,以便转换类文件。转换发生在JVM定义类之前。请注意,术语是在Java虚拟机规范的3.1节中定义的,指的是类文件格式的字节序列,无论它们是否驻留在文件中。 Instrumentation @Since:1.5Comment:该类提供了用于设计Java编程语言代码所需的服务。 仪器是向方法添加字节码,用于收集工
在进程B中向进程A中注入java agent,需要满足以下条件: java agent中的所有依赖,在进程A中的classpath中都要能找到,否则在注入时进程A会报错NoClassDefFoundError java agent的pom文件中包含如下内容,以在jar包中包含MANIFEST.MF并设置Agent-Class和Can-Retransform-C...
在仪器仪表的世界中,有一个非常要的概念,那就是仪器连接性语言(Instrument Connectivity Language,ICL)。仪器连接性语言是指用于描述仪器与其他设备之间通信和数据传输方式的语言。 仪器连接性语言通常是硬件和软件协议的组合。硬件协议指的是传输数据的物理特征,如电压、数据速率和连接器类型等;而软件协议指的是通信协议的描述,包括数据格式、传输速率、数据压缩、错误检查和校验等。 有许多种仪器连接性语言可供选择,其中包括传统的RS-232、GPIB以及现代的USB、LAN和WLAN等。选择仪器连接性语言时应该考虑到系统的复杂性、设备数量、灵活性和效率等因素。 通过使用仪器连接性语言,可以实现仪器之间的智能互联和远程控制,从而提高仪器系统的自动化程度和数据采集能力。仪器连接性语言不仅在科学研究和工程技术领域中发挥着要作用,也在医疗、安防、通信和娱乐等领域得到了广泛的应用。