即時(shí)生效原理
app啟動(dòng)到一半的時(shí)候,所有需要發(fā)生變更的類已經(jīng)被加載過了,
在Android系統(tǒng)中是無法對(duì)一個(gè)已經(jīng)加載的類進(jìn)行卸載的。騰訊的Tinker的方案是讓ClassLoader去加載新的類,如果不重啟app,原有的類還在虛擬機(jī)中,就無法加載新的類。因此需要冷啟動(dòng)后,搶先加載修復(fù)補(bǔ)丁中的新類,從而達(dá)到熱修復(fù)的目的。
AndFix采用的方法是直接在已加載的類中的native層替換掉原方法,是在原有類的基礎(chǔ)上進(jìn)行修改的。
底層替換原理
每一個(gè) Java 方法在 Art 虛擬機(jī)中都對(duì)應(yīng)一個(gè) ArtMethod,ArtMethod 記錄了該方法的所有信息,包括所屬類、訪問權(quán)限、代碼執(zhí)行地址等。
通過env->FromReflectedMethod,可以由 Method 對(duì)象得到這個(gè)方法對(duì)應(yīng)的ArtMethod的真正起始地址,然后強(qiáng)轉(zhuǎn)為 ArtMethod 指針,通過指針的操作對(duì)其成員屬性進(jìn)行修改替換。
為什么這樣替換后就可以實(shí)現(xiàn)熱修復(fù)呢?
就需要先了解虛擬機(jī)方法調(diào)用的原理
虛擬機(jī)方法調(diào)用的原理
我的測(cè)試機(jī)為Android 5.1.1,所以我們以Android 5.1.1版本的源碼為例。
Android 5.1.1版本中的 ArtMethod 結(jié)構(gòu)體路徑為:
art/runtime/mirror/art_method.h#560。其中最重要的字段就是 entry_point_from_interpreter_ 和 entry_point_from_quick_compiled_code_ ,從名字可以看出它們就是方法執(zhí)行的入口。
Java代碼在Android中會(huì)被編譯成 Dex code,
Art虛擬機(jī)中可以采用解釋模式或AOT機(jī)器碼模式執(zhí)行Dex Code。
- 解釋模式:就是取出Dex Code,逐條解釋執(zhí)行。如果方法的調(diào)用者是以解釋模式運(yùn)行的,調(diào)用該方法時(shí),就會(huì)獲取它的
entry_point_from_interpreter_,然后跳轉(zhuǎn)執(zhí)行。 - AOT模式:就會(huì)預(yù)編譯Dex code對(duì)應(yīng)的機(jī)器碼,然后在運(yùn)行期間直接執(zhí)行機(jī)器碼,不需要逐條解釋執(zhí)行dex code。如果方法的調(diào)用者是以AOT機(jī)器碼方式執(zhí)行的,在調(diào)用該方法時(shí)就是跳轉(zhuǎn)到
entry_point_from_quick_compiled_code_中執(zhí)行的。
那是不是替換方法的執(zhí)行入口就可以了呢?
當(dāng)然不是,無論是解釋模式還是AOT機(jī)器碼模式,在運(yùn)行期間還會(huì)需要調(diào)用ArtMethod中的其他成員字段
源碼跟蹤驗(yàn)證
類加載流程
- FindClass (/art/runtime/jni_internal.cc#599)
- FindClass (/art/runtime/class_linker.cc#2117)
- DefineClass (/art/runtime/class_linker.cc#2218)
- LoadClass (/art/runtime/class_linker.cc#2727)
- LoadClassMembers (/art/runtime/class_linker.cc#2767)
- LinkCode (/art/runtime/class_linker.cc#2627)
- LinkMethod (/art/runtime/oat_file.cc#595)
- NeedsInterpreter (/art/runtime/class_linker.cc#2525)
- UnregisterNative(/art/runtime/mirror/art_method.cc#364)
- UpdateMethodsCode(/art/runtime/instrumentation.cc#679)
- UpdateEntrypoints(/art/runtime/instrumentation.cc#85)
JNI類的靜態(tài)成員函數(shù) FindClass
static jclass FindClass(JNIEnv* env, const char* name) {
CHECK_NON_NULL_ARGUMENT(name);
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
std::string descriptor(NormalizeJniClassDescriptor(name));
ScopedObjectAccess soa(env);
mirror::Class* c = nullptr;
if (runtime->IsStarted()) {
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader(soa)));
c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);
} else {
c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());
}
return soa.AddLocalReference<jclass>(c);
}
在ART虛擬機(jī)進(jìn)程中,存在著一個(gè) Runtime 單例,用來描述ART運(yùn)行時(shí)。通過調(diào)用 Runtime 類的靜態(tài)成員函數(shù) Current 可以獲得上述Runtime單例。獲得了這個(gè)單例之后,就可以調(diào)用它的成員函數(shù) GetClassLinker 來獲得一個(gè) ClassLinker 對(duì)象。
ClassLinker 對(duì)象是在創(chuàng)建ART虛擬機(jī)的過程中創(chuàng)建的,用來加載類以及鏈接類方法
首先判斷ART運(yùn)行時(shí)是否已經(jīng)啟動(dòng)起來。如果已經(jīng)啟動(dòng),那么就通過調(diào)用函數(shù)GetClassLoader來獲得當(dāng)前線程所關(guān)聯(lián)的ClassLoader,并且以此為參數(shù),調(diào)用前面獲得的ClassLinker對(duì)象的成員函數(shù)FindClass來加載由參數(shù)name指定的類。
如果ART運(yùn)行時(shí)還沒有啟動(dòng),那么這時(shí)候只可以加載系統(tǒng)類。這個(gè)通過前面獲得的 ClassLinker 對(duì)象的成員函數(shù) FindSystemClass 來實(shí)現(xiàn)的。
ClassLinker類的成員函數(shù) FindClass
mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
Handle<mirror::ClassLoader> class_loader) {
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
DCHECK(self != nullptr);
self->AssertNoPendingException();
if (descriptor[1] == '\0') {
// only the descriptors of primitive types should be 1 character long, also avoid class lookup
// for primitive classes that aren't backed by dex files.
return FindPrimitiveClass(descriptor[0]);
}
const size_t hash = ComputeModifiedUtf8Hash(descriptor);
// Find the class in the loaded classes table.
mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get());
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
if (descriptor[0] == '[') {
return CreateArrayClass(self, descriptor, hash, class_loader);
} else if (class_loader.Get() == nullptr) {
// The boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
*pair.second);
} else {
// The boot class loader is searched ahead of the application class loader, failures are
// expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
// trigger the chaining with a proper stack trace.
mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(ThrowLocation(), pre_allocated);
return nullptr;
}
} else if (Runtime::Current()->UseCompileTimeClassPath()) {
// First try with the bootstrap class loader.
if (class_loader.Get() != nullptr) {
klass = LookupClass(descriptor, hash, nullptr);
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
}
// If the lookup failed search the boot class path. We don't perform a recursive call to avoid
// a NoClassDefFoundError being allocated.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
*pair.second);
}
// Next try the compile time class path.
const std::vector<const DexFile*>* class_path;
{
ScopedObjectAccessUnchecked soa(self);
ScopedLocalRef<jobject> jclass_loader(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.Get()));
class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
}
pair = FindInClassPath(descriptor, hash, *class_path);
if (pair.second != nullptr) {
return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second);
}
} else {
ScopedObjectAccessUnchecked soa(self);
mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader);
if (klass != nullptr) {
return klass;
}
ScopedLocalRef<jobject> class_loader_object(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.Get()));
std::string class_name_string(DescriptorToDot(descriptor));
ScopedLocalRef<jobject> result(soa.Env(), nullptr);
{
ScopedThreadStateChange tsc(self, kNative);
ScopedLocalRef<jobject> class_name_object(soa.Env(),
soa.Env()->NewStringUTF(class_name_string.c_str()));
if (class_name_object.get() == nullptr) {
DCHECK(self->IsExceptionPending()); // OOME.
return nullptr;
}
CHECK(class_loader_object.get() != nullptr);
result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
WellKnownClasses::java_lang_ClassLoader_loadClass,
class_name_object.get()));
}
if (self->IsExceptionPending()) {
// If the ClassLoader threw, pass that exception up.
return nullptr;
} else if (result.get() == nullptr) {
// broken loader - throw NPE to be compatible with Dalvik
ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
class_name_string.c_str()).c_str());
return nullptr;
} else {
// success, return mirror::Class*
return soa.Decode<mirror::Class*>(result.get());
}
}
ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
return nullptr;
}
參數(shù) descriptor 指向的是要加載的類的簽名,而參數(shù) class_loader 指向的是一個(gè)類加載器。
首先是調(diào)用另外一個(gè)成員函數(shù) LookupClass 來檢查參數(shù) descriptor 指定的類是否已經(jīng)被加載過。如果是的話,那么 ClassLinker 類的成員函數(shù) LookupClass 就會(huì)返回一個(gè)對(duì)應(yīng)的 Class 對(duì)象,這個(gè) Class 對(duì)象接著就會(huì)返回給調(diào)用者,表示加載已經(jīng)完成。
如果參數(shù) descriptor 指定的類還沒有被加載過,這時(shí)候主要就是要看參數(shù) class_loader 的值了。如果參數(shù) class_loader 的值等于NULL,那么就需要調(diào)用 FindInClassPath 來在系統(tǒng)啟動(dòng)類路徑尋找對(duì)應(yīng)的類。一旦尋找到,那么就會(huì)獲得包含目標(biāo)類的DEX文件,因此接下來就調(diào)用 ClassLinker 類的另外一個(gè)成員函數(shù) DefineClass 從獲得的DEX文件中加載參數(shù) descriptor 指定的類了。
知道了參數(shù) descriptor 指定的類定義在哪一個(gè)DEX文件之后,就可以通過 ClassLinker 類的另外一個(gè)成員函數(shù)
DefineClass 來從中加載它了。
ClassLinker類的成員函數(shù) DefineClass
mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
StackHandleScope<3> hs(self);
auto klass = hs.NewHandle<mirror::Class>(nullptr);
// Load the class from the dex file.
if (UNLIKELY(!init_done_)) {
// finish up init of hand crafted class_roots_
if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
klass.Assign(GetClassRoot(kJavaLangObject));
} else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
klass.Assign(GetClassRoot(kJavaLangClass));
} else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
klass.Assign(GetClassRoot(kJavaLangString));
} else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) {
klass.Assign(GetClassRoot(kJavaLangRefReference));
} else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {
klass.Assign(GetClassRoot(kJavaLangDexCache));
} else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) {
klass.Assign(GetClassRoot(kJavaLangReflectArtField));
} else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) {
klass.Assign(GetClassRoot(kJavaLangReflectArtMethod));
}
}
if (klass.Get() == nullptr) {
// Allocate a class with the status of not ready.
// Interface object should get the right size here. Regular class will
// figure out the right size later and be replaced with one of the right
// size when the class becomes resolved.
klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
}
if (UNLIKELY(klass.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // Expect an OOME.
return nullptr;
}
klass->SetDexCache(FindDexCache(dex_file));
LoadClass(dex_file, dex_class_def, klass, class_loader.Get());
ObjectLock<mirror::Class> lock(self, klass);
if (self->IsExceptionPending()) {
// An exception occured during load, set status to erroneous while holding klass' lock in case
// notification is necessary.
if (!klass->IsErroneous()) {
klass->SetStatus(mirror::Class::kStatusError, self);
}
return nullptr;
}
klass->SetClinitThreadId(self->GetTid());
// Add the newly loaded class to the loaded classes table.
mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash);
if (existing != nullptr) {
// We failed to insert because we raced with another thread. Calling EnsureResolved may cause
// this thread to block.
return EnsureResolved(self, descriptor, existing);
}
// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
klass->SetStatus(mirror::Class::kStatusError, self);
}
return nullptr;
}
CHECK(klass->IsLoaded());
// Link the class (if necessary)
CHECK(!klass->IsResolved());
// TODO: Use fast jobjects?
auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
mirror::Class* new_class = nullptr;
if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) {
// Linking failed.
if (!klass->IsErroneous()) {
klass->SetStatus(mirror::Class::kStatusError, self);
}
return nullptr;
}
self->AssertNoPendingException();
CHECK(new_class != nullptr) << descriptor;
CHECK(new_class->IsResolved()) << descriptor;
Handle<mirror::Class> new_class_h(hs.NewHandle(new_class));
/*
* We send CLASS_PREPARE events to the debugger from here. The
* definition of "preparation" is creating the static fields for a
* class and initializing them to the standard default values, but not
* executing any code (that comes later, during "initialization").
*
* We did the static preparation in LinkClass.
*
* The class has been prepared and resolved but possibly not yet verified
* at this point.
*/
Dbg::PostClassPrepare(new_class_h.Get());
return new_class_h.Get();
}
ClassLinker類有一個(gè)類型為bool的成員變量 init_done_,用來表示ClassLinker是否已經(jīng)初始化完成。
如果ClassLinker正處于初始化過程,即其成員變量 init_done_ 的值等于false,并且參數(shù) descriptor 描述的是特定的內(nèi)部類,那么就將本地變量 klass 指向它們,其余情況則會(huì)通過成員函數(shù) AllocClass 為其分配存儲(chǔ)空間,以便后面通過成員函數(shù) LoadClass 進(jìn)行初始化。
ClassLinker類的成員函數(shù) LoadClass 用來從指定的DEX文件中加載指定的類。指定的類從DEX文件中加載完成后,需要通過另外一個(gè)成員函數(shù) InsertClass 添加到 ClassLinker 的已加載類列表中去。如果指定的類之前已經(jīng)加載過,即調(diào)用成員函數(shù)InsertClass得到的返回值不等于空,那么就說明有另外的一個(gè)線程也正在加載指定的類。這時(shí)候就需要調(diào)用成員函數(shù)EnsureResolved來保證(等待)該類已經(jīng)加載并且解析完成。另一方面,如果沒有其它線程加載指定的類,那么當(dāng)前線程從指定的DEX文件加載完成指定的類后,還需要調(diào)用成員函數(shù)LinkClass來對(duì)加載后的類進(jìn)行解析。最后,一個(gè)類型為Class的對(duì)象就可以返回給調(diào)用者了,用來表示一個(gè)已經(jīng)加載和解析完成的類。
ClassLinker類的成員函數(shù) LoadClass
void ClassLinker::LoadClass(const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass,
mirror::ClassLoader* class_loader) {
CHECK(klass.Get() != nullptr);
CHECK(klass->GetDexCache() != nullptr);
CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());
const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
CHECK(descriptor != nullptr);
klass->SetClass(GetClassRoot(kJavaLangClass));
if (kUseBakerOrBrooksReadBarrier) {
klass->AssertReadBarrierPointer();
}
uint32_t access_flags = dex_class_def.GetJavaAccessFlags();
CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U);
klass->SetAccessFlags(access_flags);
klass->SetClassLoader(class_loader);
DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
klass->SetStatus(mirror::Class::kStatusIdx, nullptr);
klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
klass->SetDexTypeIndex(dex_class_def.class_idx_);
CHECK(klass->GetDexCacheStrings() != nullptr);
const byte* class_data = dex_file.GetClassData(dex_class_def);
if (class_data == nullptr) {
return; // no fields or methods - for example a marker interface
}
OatFile::OatClass oat_class;
if (Runtime::Current()->IsStarted()
&& !Runtime::Current()->UseCompileTimeClassPath()
&& FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) {
LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class);
} else {
LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr);
}
}
dex_file: 類型為DexFile,描述要加載的類所在的DEX文件。
dex_class_def: 類型為ClassDef,描述要加載的類在DEX文件里面的信息。
klass: 類型為Class,描述加載完成的類。
class_loader: 類型為ClassLoader,描述所使用的類加載器。
LoadClass的任務(wù)就是要用dex_file、dex_class_def、class_loader三個(gè)參數(shù)包含的相關(guān)信息設(shè)置到參數(shù)klass描述的Class對(duì)象去,以便可以得到一個(gè)完整的已加載類信息。
關(guān)鍵:
- setClassLoader:將class_loader描述的ClassLoader設(shè)置到klass描述的Class對(duì)象中,即給每一個(gè)已加載的類關(guān)聯(lián)一個(gè)類加載器。
- SetDexClassDefIndex:通過DexFile的成員函數(shù)GetIndexForClassDef獲得正在加載的類在Dex文件中的索引號(hào),并設(shè)置到klass中
FindOatClass:從相應(yīng)的OAT文件中找到與正在加載的類對(duì)應(yīng)的一個(gè)OatClass結(jié)構(gòu)體oat_class。這需要利用到上面提到的DEX類索引號(hào),這是因?yàn)镈EX類和OAT類根據(jù)索引號(hào)存在一一對(duì)應(yīng)關(guān)系。
ClassLinker類的成員函數(shù) LoadClassMembers
void ClassLinker::LoadClassMembers(const DexFile& dex_file,
const byte* class_data,
Handle<mirror::Class> klass,
mirror::ClassLoader* class_loader,
const OatFile::OatClass* oat_class) {
// Load fields.
ClassDataItemIterator it(dex_file, class_data);
Thread* self = Thread::Current();
if (it.NumStaticFields() != 0) {
mirror::ObjectArray<mirror::ArtField>* statics = AllocArtFieldArray(self, it.NumStaticFields());
if (UNLIKELY(statics == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
klass->SetSFields(statics);
}
if (it.NumInstanceFields() != 0) {
mirror::ObjectArray<mirror::ArtField>* fields =
AllocArtFieldArray(self, it.NumInstanceFields());
if (UNLIKELY(fields == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
klass->SetIFields(fields);
}
for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtField> sfield(hs.NewHandle(AllocArtField(self)));
if (UNLIKELY(sfield.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
klass->SetStaticField(i, sfield.Get());
LoadField(dex_file, it, klass, sfield);
}
for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtField> ifield(hs.NewHandle(AllocArtField(self)));
if (UNLIKELY(ifield.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
klass->SetInstanceField(i, ifield.Get());
LoadField(dex_file, it, klass, ifield);
}
// Load methods.
if (it.NumDirectMethods() != 0) {
// TODO: append direct methods to class object
mirror::ObjectArray<mirror::ArtMethod>* directs =
AllocArtMethodArray(self, it.NumDirectMethods());
if (UNLIKELY(directs == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
klass->SetDirectMethods(directs);
}
if (it.NumVirtualMethods() != 0) {
// TODO: append direct methods to class object
mirror::ObjectArray<mirror::ArtMethod>* virtuals =
AllocArtMethodArray(self, it.NumVirtualMethods());
if (UNLIKELY(virtuals == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
klass->SetVirtualMethods(virtuals);
}
size_t class_def_method_index = 0;
uint32_t last_dex_method_index = DexFile::kDexNoIndex;
size_t last_class_def_method_index = 0;
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
if (UNLIKELY(method.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
klass->SetDirectMethod(i, method.Get());
LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
uint32_t it_method_index = it.GetMemberIndex();
if (last_dex_method_index == it_method_index) {
// duplicate case
method->SetMethodIndex(last_class_def_method_index);
} else {
method->SetMethodIndex(class_def_method_index);
last_dex_method_index = it_method_index;
last_class_def_method_index = class_def_method_index;
}
class_def_method_index++;
}
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
StackHandleScope<1> hs(self);
Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
if (UNLIKELY(method.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return;
}
klass->SetVirtualMethod(i, method.Get());
DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
class_def_method_index++;
}
DCHECK(!it.HasNext());
}
從參數(shù)dex_file描述的DEX文件中獲得正在加載的類的靜態(tài)成員變量和實(shí)例成員變量個(gè)數(shù),并且為每一個(gè)靜態(tài)成員變量和實(shí)例成員變量都分配一個(gè)ArtField對(duì)象,接著通過ClassLinker類的成員函數(shù)LoadField對(duì)這些ArtField對(duì)象進(jìn)行初始化。初始化得到的ArtField對(duì)象全部保存在klass描述的Class對(duì)象中。
從參數(shù)dex_file描述的DEX文件中獲得正在加載的類的直接成員函數(shù)和虛擬成員函數(shù)個(gè)數(shù),并且為每一個(gè)直接成員函數(shù)和虛擬成員函數(shù)都分配一個(gè)ArtMethod對(duì)象,接著通過ClassLinker類的成員函數(shù)LoadMethod對(duì)這些ArtMethod對(duì)象進(jìn)行初始化。初始好得到的ArtMethod對(duì)象全部保存在klass描述的Class對(duì)象中。
參數(shù)klass描述的Class對(duì)象包含了一系列的ArtField對(duì)象和ArtMethod對(duì)象,其中,ArtField對(duì)象用來描述成員變量信息,而ArtMethod用來描述成員函數(shù)信息。
接下來繼續(xù)分析LinkCode函數(shù)的實(shí)現(xiàn),以便可以了解如何在一個(gè)OAT文件中找到一個(gè)DEX類方法的本地機(jī)器指令。
ClassLinker類的成員函數(shù) LinkCode
void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
const DexFile& dex_file, uint32_t dex_method_index,
uint32_t method_index) {
if (Runtime::Current()->IsCompiler()) {
// The following code only applies to a non-compiler runtime.
return;
}
// Method shouldn't have already been linked.
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
#if defined(ART_USE_PORTABLE_COMPILER)
DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr);
#endif
if (oat_class != nullptr) {
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
oat_method.LinkMethod(method.Get());
}
// Install entry point from interpreter.
bool enter_interpreter = NeedsInterpreter(method.Get(),
method->GetEntryPointFromQuickCompiledCode(),
#if defined(ART_USE_PORTABLE_COMPILER)
method->GetEntryPointFromPortableCompiledCode());
#else
nullptr);
#endif
if (enter_interpreter && !method->IsNative()) {
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
} else {
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
}
if (method->IsAbstract()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
#endif
return;
}
bool have_portable_code = false;
if (method->IsStatic() && !method->IsConstructor()) {
// For static methods excluding the class initializer, install the trampoline.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline());
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline());
#endif
} else if (enter_interpreter) {
if (!method->IsNative()) {
// Set entry point from compiled code if there's no code or in interpreter only mode.
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
#endif
} else {
method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniTrampoline());
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
#endif
}
#if defined(ART_USE_PORTABLE_COMPILER)
} else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) {
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
have_portable_code = true;
method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
#endif
} else {
DCHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
#endif
}
if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative(Thread::Current());
if (enter_interpreter) {
// We have a native method here without code. Then it should have either the GenericJni
// trampoline as entrypoint (non-static), or the Resolution trampoline (static).
DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickResolutionTrampoline()
|| method->GetEntryPointFromQuickCompiledCode() == GetQuickGenericJniTrampoline());
}
}
// Allow instrumentation its chance to hijack code.
Runtime* runtime = Runtime::Current();
runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
method->GetEntryPointFromQuickCompiledCode(),
#if defined(ART_USE_PORTABLE_COMPILER)
method->GetEntryPointFromPortableCompiledCode(),
#else
nullptr,
#endif
have_portable_code);
}
- 參數(shù)method表示要設(shè)置本地機(jī)器指令的類方法。
- 參數(shù)oat_class表示類方法method在OAT文件中對(duì)應(yīng)的OatClass結(jié)構(gòu)體。
- 參數(shù)dex_file表示在dex文件中對(duì)應(yīng)的DexFile。
- 參數(shù)dex_method_index表示DexFile中方法method的索引號(hào)。
- 參數(shù)method_index表示類方法method的索引號(hào)。
通過參數(shù) method_index 描述的索引號(hào)可以在 oat_class 表示的OatClass結(jié)構(gòu)體中找到一個(gè) OatMethod 結(jié)構(gòu)體 oat_method。這個(gè) OatMethod 結(jié)構(gòu)描述了類方法method的本地機(jī)器指令相關(guān)信息,通過調(diào)用它的成員函數(shù)LinkMethod可以將這些信息設(shè)置到參數(shù)method描述的 ArtMethod 對(duì)象中去。
OatMethod 的成員函數(shù) LinkMethod
void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const {
CHECK(method != NULL);
#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableCode());
#endif
method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
}
通過OatMethod類的成員函數(shù) GetPortableCode 和 GetQuickCode 獲得 OatMethod 結(jié)構(gòu)體中的 code_offset_ 字段,并且通過調(diào)用 ArtMethod 類的成員函數(shù) SetEntryPointFromCompiledCode 設(shè)置到參數(shù)method描述的 ArtMethod 對(duì)象中去。OatMethod 結(jié)構(gòu)體中的 code_offset_ 字段指向的是一個(gè)本地機(jī)器指令函數(shù),這個(gè)本地機(jī)器指令函數(shù)正是通過翻譯參數(shù)method描述的類方法的DEX字節(jié)碼得到的。
OatMethod 的 GetPortableCode 函數(shù)
const void* GetPortableCode() const {
// TODO: encode whether code is portable/quick in flags within OatMethod.
if (kUsePortableCompiler) {
return GetOatPointer<const void*>(code_offset_);
} else {
return nullptr;
}
}
OatMethod 的 GetQuickCode 函數(shù)
const void* GetQuickCode() const {
if (kUsePortableCompiler) {
return nullptr;
} else {
return GetOatPointer<const void*>(code_offset_);
}
}
ClassLinker類的全局函數(shù) NeedsInterpreter
// Returns true if the method must run with interpreter, false otherwise.
static bool NeedsInterpreter(
mirror::ArtMethod* method, const void* quick_code, const void* portable_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if ((quick_code == nullptr) && (portable_code == nullptr)) {
// No code: need interpreter.
// May return true for native code, in the case of generic JNI
// DCHECK(!method->IsNative());
return true;
}
#ifdef ART_SEA_IR_MODE
ScopedObjectAccess soa(Thread::Current());
if (std::string::npos != PrettyMethod(method).find("fibonacci")) {
LOG(INFO) << "Found " << PrettyMethod(method);
return false;
}
#endif
// If interpreter mode is enabled, every method (except native and proxy) must
// be run with interpreter.
return Runtime::Current()->GetInstrumentation()->InterpretOnly() &&
!method->IsNative() && !method->IsProxyMethod();
}
檢查參數(shù)method描述的類方法是否需要通過解釋器執(zhí)行。
在以下兩種情況下,一個(gè)類方法需要通過解釋器來執(zhí)行:
- 沒有對(duì)應(yīng)的本地機(jī)器指令,即參數(shù)quick_code和portable_code的值等于NULL。
- ART虛擬機(jī)運(yùn)行在解釋模式中,并且類方法不是JNI方法,并且也不是代理方法。
因?yàn)镴NI方法是沒有對(duì)應(yīng)的DEX字節(jié)碼的,因此即使ART虛擬機(jī)運(yùn)行在解釋模式中,JNI方法也不能通過解釋器來執(zhí)行。至于代理方法,由于是動(dòng)態(tài)生成的(沒有對(duì)應(yīng)的DEX字節(jié)碼),因此即使ART虛擬機(jī)運(yùn)行在解釋模式中,它們也不通過解釋器來執(zhí)行。
調(diào)用Runtime類的靜態(tài)成員函數(shù)Current獲得的是描述ART運(yùn)行時(shí)的一個(gè)Runtime對(duì)象。調(diào)用這個(gè)Runtime對(duì)象的成員函數(shù)GetInstrumentation獲得的是一個(gè)Instrumentation對(duì)象。這個(gè)Instrumentation對(duì)象是用來調(diào)試ART運(yùn)行時(shí)的,通過調(diào)用它的成員函數(shù)InterpretOnly可以知道ART虛擬機(jī)是否運(yùn)行在解釋模式中。
回到ClassLinker類的成員函數(shù) LinkCode
如果調(diào)用函數(shù)NeedsInterpreter得到的返回值enter_interpreter等于true,而且不是Native方法,那么就意味著參數(shù)method描述的類方法需要通過解釋器來執(zhí)行,這時(shí)候就將函數(shù)artInterpreterToInterpreterBridge設(shè)置為解釋器執(zhí)行該類方法的入口點(diǎn)。否則的話,就將另外一個(gè)函數(shù)artInterpreterToCompiledCodeBridge設(shè)置為解釋器執(zhí)行該類方法的入口點(diǎn)。
為什么我們需要為類方法設(shè)置解釋器入口點(diǎn)呢?根據(jù)前面的分析可以知道,在ART虛擬機(jī)中,并不是所有的類方法都是有對(duì)應(yīng)的本地機(jī)器指令的,并且即使一個(gè)類方法有對(duì)應(yīng)的本地機(jī)器指令,當(dāng)ART虛擬機(jī)以解釋模式運(yùn)行時(shí),它也需要通過解釋器來執(zhí)行。當(dāng)以解釋器執(zhí)行的類方法在執(zhí)行的過程中調(diào)用了其它的類方法時(shí),解釋器就需要進(jìn)一步知道被調(diào)用的類方法是應(yīng)用以解釋方式執(zhí)行,還是本地機(jī)器指令方法執(zhí)行。為了能夠進(jìn)行統(tǒng)一處理,就給每一個(gè)類方法都設(shè)置一個(gè)解釋器入口點(diǎn)。需要通過解釋執(zhí)行的類方法的解釋器入口點(diǎn)函數(shù)是artInterpreterToInterpreterBridge,它會(huì)繼續(xù)通過解釋器來執(zhí)行該類方法。需要通過本地機(jī)器指令執(zhí)行的類方法的解釋器入口點(diǎn)函數(shù)是artInterpreterToCompiledCodeBridge,它會(huì)間接地調(diào)用該類方法的本地機(jī)器指令。
判斷method是否是一個(gè)抽象方法。抽象方法聲明類中是沒有實(shí)現(xiàn)的,必須要由子類實(shí)現(xiàn)。因此抽象方法在聲明類中是沒有對(duì)應(yīng)的本地機(jī)器指令的,它們必須要通過解釋器來執(zhí)行。不過,為了能夠進(jìn)行統(tǒng)一處理,我們?nèi)匀患傺b抽象方法有對(duì)應(yīng)的本地機(jī)器指令函數(shù),只不過這個(gè)本地機(jī)器指令函數(shù)被設(shè)置為 GetQuickToInterpreterBridge。當(dāng)函數(shù) GetQuickToInterpreterBridge,就會(huì)自動(dòng)進(jìn)入到解釋器中去。
當(dāng)method是一個(gè)非類靜態(tài)初始化函數(shù)(class initializer)的靜態(tài)方法時(shí),我們不能直接執(zhí)行翻譯其DEX字節(jié)碼得到的本地機(jī)器指令。這是因?yàn)轭愳o態(tài)方法可以在不創(chuàng)建類對(duì)象的前提下執(zhí)行。這意味著一個(gè)類靜態(tài)方法在執(zhí)行的時(shí)候,對(duì)應(yīng)的類可能還沒有初始化好。這時(shí)候我們就需要先將對(duì)應(yīng)的類初始化好,再執(zhí)行相應(yīng)的靜態(tài)方法。為了能夠做到這一點(diǎn)。我們就先調(diào)用GetResolutionTrampoline函數(shù)得到一個(gè)Tampoline函數(shù),接著將這個(gè)Trampoline函數(shù)作為靜態(tài)方法的本地機(jī)器指令。這樣如果類靜態(tài)方法在對(duì)應(yīng)的類初始化前被調(diào)用,就會(huì)觸發(fā)上述的Trampoline函數(shù)被執(zhí)行。而當(dāng)上述Trampoline函數(shù)執(zhí)行時(shí),它們先初始化好對(duì)應(yīng)的類,再調(diào)用原來的類靜態(tài)方法對(duì)應(yīng)的本地機(jī)器指令。按照代碼中的注釋,當(dāng)一個(gè)類初始化完成之后,就可以調(diào)用函數(shù)ClassLinker::FixupStaticTrampolines來修復(fù)該類的靜態(tài)成員函數(shù)的本地機(jī)器指令,也是通過翻譯DEX字節(jié)碼得到的本地機(jī)器指令。這里需要注意的是,為什么類靜態(tài)初始化函數(shù)不需要按照其它的類靜態(tài)方法一樣設(shè)置Tampoline函數(shù)呢?這是因?yàn)轭愳o態(tài)初始化函數(shù)是一定保證是在類初始化過程中執(zhí)行的。
當(dāng)method需要通過解釋器執(zhí)行時(shí),那么當(dāng)該類方法執(zhí)行時(shí),就不能執(zhí)行它的本地機(jī)器指令,因此我們就先調(diào)用GetCompiledCodeToInterpreterBridge函數(shù)獲得一個(gè)橋接函數(shù),并且將這個(gè)橋接函數(shù)假裝為類方法的本地機(jī)器指令。一旦該橋接函數(shù)被執(zhí)行,它就會(huì)入到解釋器去執(zhí)行類方法。通過這種方式,我們就可以以統(tǒng)一的方法來調(diào)用解釋執(zhí)行和本地機(jī)器指令執(zhí)行的類方法。
判斷method是否是一個(gè)JNI方法。如果是的話,那么就調(diào)用ArtMethod類的成員函數(shù)UnregisterNative來初始化它的JNI方法調(diào)用接口。
ArtMethod類的成員函數(shù) UnregisterNative
void ArtMethod::UnregisterNative(Thread* self) {
CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this);
// restore stub to lookup native pointer via dlsym
RegisterNative(self, GetJniDlsymLookupStub(), false);
}
UnregisterNative實(shí)際上就是將一個(gè)JNI方法的初始化入口設(shè)置為通過調(diào)用函數(shù)GetJniDlsymLookupStub獲得的一個(gè)Stub。這個(gè)Stub的作用是,當(dāng)一個(gè)JNI方法被調(diào)用時(shí),如果還沒有顯示地注冊(cè)有Native函數(shù),那么它就會(huì)自動(dòng)從已加載的SO文件查找是否存在一個(gè)對(duì)應(yīng)的Native函數(shù)。如果存在的話,就將它注冊(cè)為JNI方法的Native函數(shù),并且執(zhí)行它。這就是隱式的JNI方法注冊(cè)。
回到ClassLinker類的成員函數(shù) LinkCode
最后調(diào)用Instrumentation類的成員函數(shù)UpdateMethodsCode檢查是否要進(jìn)一步修改參數(shù)method描述的類方法的本地機(jī)器指令入口。
void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code,
const void* portable_code, bool have_portable_code) {
const void* new_portable_code;
const void* new_quick_code;
bool new_have_portable_code;
if (LIKELY(!instrumentation_stubs_installed_)) {
new_portable_code = portable_code;
new_quick_code = quick_code;
new_have_portable_code = have_portable_code;
} else {
if ((interpreter_stubs_installed_ || IsDeoptimized(method)) && !method->IsNative()) {
#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = GetPortableToInterpreterBridge();
#else
new_portable_code = portable_code;
#endif
new_quick_code = GetQuickToInterpreterBridge();
new_have_portable_code = false;
} else {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (quick_code == class_linker->GetQuickResolutionTrampoline() ||
quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() ||
quick_code == GetQuickToInterpreterBridge()) {
#if defined(ART_USE_PORTABLE_COMPILER)
DCHECK((portable_code == class_linker->GetPortableResolutionTrampoline()) ||
(portable_code == GetPortableToInterpreterBridge()));
#endif
new_portable_code = portable_code;
new_quick_code = quick_code;
new_have_portable_code = have_portable_code;
} else if (entry_exit_stubs_installed_) {
new_quick_code = GetQuickInstrumentationEntryPoint();
#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = GetPortableToInterpreterBridge();
#else
new_portable_code = portable_code;
#endif
new_have_portable_code = false;
} else {
new_portable_code = portable_code;
new_quick_code = quick_code;
new_have_portable_code = have_portable_code;
}
}
}
UpdateEntrypoints(method, new_quick_code, new_portable_code, new_have_portable_code);
}
Instrumentation類是用來調(diào)用ART運(yùn)行時(shí)的。例如,當(dāng)我們需要監(jiān)控類方法的調(diào)用時(shí),就可以往Instrumentation注冊(cè)一些Listener。這樣當(dāng)類方法調(diào)用時(shí),這些注冊(cè)的Listener就會(huì)得到回調(diào)。當(dāng)Instrumentation注冊(cè)有相應(yīng)的Listener時(shí),它的成員變量instrumentation_stubs_installed_的值就會(huì)等于true。
總結(jié):
通過上述源碼跟蹤分析,一個(gè)類的加載過程完成了。加載完成后得到的是一個(gè)Class對(duì)象。這個(gè)Class對(duì)象關(guān)聯(lián)有一系列的 ArtField 對(duì)象和 ArtMethod 對(duì)象。其中,ArtField對(duì)象描述的是成員變量,而ArtMethod對(duì)象描述的是成員函數(shù)。對(duì)于每一個(gè)ArtMethod對(duì)象,它都有一個(gè)解釋器入口點(diǎn)和一個(gè)本地機(jī)器指令入口點(diǎn)。這樣,無論一個(gè)類方法是通過解釋器執(zhí)行,還是直接以本地機(jī)器指令執(zhí)行,我們都可以以統(tǒng)一的方式來進(jìn)行調(diào)用。同時(shí),理解了上述的類加載過程后,我們就可以知道,我們?cè)贜ative層通過JNI接口FindClass查找或者加載類時(shí),得到的一個(gè)不透明的jclass值,實(shí)際上指向的是一個(gè)Class對(duì)象。
類方法查找流程
- GetStaticMethodID (/art/runtime/jni_internal.cc#943)
- FindMethodID (/art/runtime/jni_internal.cc#140)
JNI類的靜態(tài)成員函數(shù) FindClass
static jmethodID GetStaticMethodID(JNIEnv* env, jclass java_class, const char* name,
const char* sig) {
CHECK_NON_NULL_ARGUMENT(java_class);
CHECK_NON_NULL_ARGUMENT(name);
CHECK_NON_NULL_ARGUMENT(sig);
ScopedObjectAccess soa(env);
return FindMethodID(soa, java_class, name, sig, true);
}
參數(shù) name 和 sig 描述的分別是要查找的類方法的名稱和簽名,而參數(shù) java_class 是對(duì)應(yīng)的類。參數(shù) java_class 的類型是jclass,從前面類加載過程的分析可以知道,它實(shí)際上指向的是一個(gè)Class對(duì)象。
JNI類的靜態(tài)成員函數(shù) FindMethodID
static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
const char* name, const char* sig, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(jni_class));
if (c == nullptr) {
return nullptr;
}
mirror::ArtMethod* method = nullptr;
if (is_static) {
method = c->FindDirectMethod(name, sig);
} else if (c->IsInterface()) {
method = c->FindInterfaceMethod(name, sig);
} else {
method = c->FindVirtualMethod(name, sig);
if (method == nullptr) {
// No virtual method matching the signature. Search declared
// private methods and constructors.
method = c->FindDeclaredDirectMethod(name, sig);
}
}
if (method == nullptr || method->IsStatic() != is_static) {
ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static");
return nullptr;
}
return soa.EncodeMethod(method);
}
執(zhí)行過程:
- 將參數(shù)jni_class的值轉(zhuǎn)換為一個(gè)Class指針c,因此就可以得到一個(gè)Class對(duì)象,并且通過ClassLinker類的成員函數(shù)EnsureInitialized確保該Class對(duì)象描述的類已經(jīng)初始化。
- Class對(duì)象c描述的類在加載的過程中,經(jīng)過解析已經(jīng)關(guān)聯(lián)上一系列的成員函數(shù)。這些成員函數(shù)可以分為兩類:Direct和Virtual。Direct類的成員函數(shù)包括所有的靜態(tài)成員函數(shù)、私有成員函數(shù)和構(gòu)造函數(shù),而Virtual則包括所有的虛成員函數(shù)。
- 經(jīng)過前面的查找過程,如果都不能在Class對(duì)象c描述的類中找到與參數(shù)name和sig對(duì)應(yīng)的成員函數(shù),那么就拋出一個(gè)NoSuchMethodError異常。否則的話,就將查找得到的ArtMethod對(duì)象封裝成一個(gè)jmethodID值返回給調(diào)用者。
我們通過調(diào)用JNI接口GetStaticMethodID獲得的不透明jmethodID值指向的實(shí)際上是一個(gè)ArtMethod對(duì)象。
當(dāng)我們獲得了一個(gè)ArtMethod對(duì)象之后,就可以輕松地得到它的本地機(jī)器指令入口,進(jìn)而對(duì)它進(jìn)行執(zhí)行。
總結(jié):
當(dāng)我們把舊方法(ArtMethod)的所有成員字段都替換為新方法(ArtMethod)的成員字段后,執(zhí)行時(shí)所有的數(shù)據(jù)就可以保持和新方法的數(shù)據(jù)一致。這樣在所有執(zhí)行到舊方法的地方,會(huì)獲取新方法的執(zhí)行入口、所屬類型、方法索引號(hào)、以及所屬dex信息,然后像調(diào)用舊方法一樣,執(zhí)行新方法的邏輯。