java 重定义_Java探针技术-动态重定义Class

在上篇博客(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对象

对比新老类,并要求如下:

父类是同一个

实现的接口数也要相同,并且是相同的接口

类访问符必须一致

字段数和字段名要一致

新增的方法必须是private static/final的

可以删除修改方法

对新类做字节码校验

第二是doit() 这是在VMThread里执行的主要是替换原来的class里的内容

合并常量

清除原来类上的断点

清除原来类上的JIT优化

将老的jmethodId更新到新类的jmethodid上

将新类的常量池的hodler指向老的类

将新类和老类的一些属性做交换,比如常量池,methods,内部类

初始化新的vtable和itable

交换annotation的method、field、parameter

遍历所有当前类的子类,修改他们的vtable及itable

第三是doit_epilogue()

主要是释放前面的步骤内存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值