Java探針技術-動態重定義Class

  
  在上篇博客(http://blog.csdn.net/raintungli/article/details/51646556)中提到了在on_attach的方式上如何從新定義class,裏面也提到了最後attach時候會調用咱們自定義的agent class的agentmain方法,在Instrumentation的接口裏面實際上自己提供了redfineClasses的方法,也就是agentmain的方法只是一個調用入口,仍是須要調用sun自己提供的Instrumentation 的redfineClasses的方法去替換classes java

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的方法jvm

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的方法spa

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.net

第一是doit_prologuecode

這個被在JavaThread裏調用主要是解析和校驗傳遞過來的bytecode生成scrash_class
挨個遍歷要批量重定義的jvmtiClassDefinition
而後讀取新的字節碼,若是有關注ClassFileLoadHook事件的,還會走對應的transform來對新的字節碼再作修改
字節碼解析好,建立一個klassOop對象

 對比新老類,並要求以下:orm

父類是同一個
實現的接口數也要相同,而且是相同的接口
類訪問符必須一致
字段數和字段名要一致
新增的方法必須是private static/final的
能夠刪除修改方法
對新類作字節碼校驗

第二是doit() 這是在VMThread裏執行的主要是替換原來的class裏的內容對象

合併常量
清除原來類上的斷點
清除原來類上的JIT優化
將老的jmethodId更新到新類的jmethodid上
將新類的常量池的hodler指向老的類
將新類和老類的一些屬性作交換,好比常量池,methods,內部類
初始化新的vtable和itable
交換annotation的method、field、parameter
遍歷全部當前類的子類,修改他們的vtable及itable

第三是doit_epilogue() 
  blog

主要是釋放前面的步驟內存接口

相關文章
相關標籤/搜索