在上篇博客(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);
.....
}
}
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 */
RedefineClasses 分为3部分,参考jvmtiRedefineClasses.cpp
第一是doit_prologue 这个被在JavaThread里调用主要是解析和校验传递过来的bytecode生成scrash_class