ART运行时之method/field加载
先简单介绍下OAT的文件格式,后面针对Method/Field/JNI方法加载做些代码分析。OAT文件格式在虚拟机以ART模式运行的Android手机上,apk安装时,系统通过installed调用dex2oat将apk中的classes.dex优化成目标机器的本地机器指令,优化的oat文件放在/data/dalvik-cache目录下。OAT文件是ELF格式文件,其包含2个重要的Secti
先简单介绍下OAT的文件格式,后面针对Method/Field/JNI方法加载做些代码分析。
OAT文件格式
在虚拟机以ART模式运行的Android手机上,apk安装时,系统通过installed调用dex2oat将apk中的classes.dex优化成目标机器的本地机器指令,优化的oat文件放在/data/dalvik-cache目录下。
OAT文件是ELF格式文件,其包含2个重要的Section,一个是oatdata段,保存了被优化的原.dex文件(Dex File Content),以及一个用来找到方法的索引(OatClass)。通过OatClass段,可以找到本地机器指令的偏移。例如,给定一个类A,那么从Dex文件中可以拿到这个类的所有信息,包括父类,成员变量,函数等。给定一个类及其某一个方法的签名后,就可以通过dex文件内容和OatClass结构体找到在oatexec段的本地机器指令,然后就可以调用这个方法的本地指令了。
一个OAT文件可以包括1到多个DEX文件。
方法调用“狸猫换太子”
这里先介绍下caller/callee方法在同一个dex中的方法/field访问,跨dex调用相对简单很多,后面会介绍下。以下是ART访问method/field用到的主要结构体:
Class是类在内存中的一个映射,包括类中包含的instant field, static fields, virtual methods table, direct methods table, interface methods tables等。
DexFile类是Dex文件在内存中的一个映射,其实就是parse Dex的结果。通过它可以找到DexFile中各个成员的名字和在各个段中的偏移。(可以参考Dex文件结构)
DexCache类是一个buffer,用来保存已经访问过的fields, methods等。
ArtMethod保存了任何一个方法时的入口(native code或者“绷床”函数“)
ArtField类似ArtMethod。
每个Class包含了一个ifTable数组,一个ifTable对应Class直接implement的一个interface,或者通过super class间接implement的一个interface。一个ifTable包含了一个ArtMethod数组,每个ArtMethod对应一个implement 的方法,这个方法可能是当前类实现的,也可以是super类implement的。
每个Class包含了一个vTable,这个vTable也有一个ArtMethod数组,包含了所有的Public方法以及从直接父类或者间接父类继承过来的public方法。
direct methods也有一个ArtMethod数组,包含了constructor函数和private函数。
ClassLoader是Load当前类的ClassLoader(native层)。
ART调用方法时用到的最重要的2个结构体就是DEX Cache和ART Method。
File:dex_cache.cc
class MANAGED DexCache FINAL : public Object {
.....
private:
HeapReference<Object> dex_;
HeapReference<String> location_;
// Either an int array or long array based on runtime ISA since these arrays hold pointers.
HeapReference<PointerArray> resolved_fields_;
HeapReference<PointerArray> resolved_methods_;
HeapReference<ObjectArray<Class>> resolved_types_;
HeapReference<ObjectArray<String>> strings_;
uint64_t dex_file_;
}
每个Dex File在内存中都会对应有一个DEX Cache,例如蘑菇街app有5个multidex,那么虚拟机内存中就会有5个Dex Cache。Dex Cache中保存了对应的DEX中的域(fields), 方法(methods), 类型(types), 字符串(String)。
Dex Cache其实就是一个buffer用来保存已经解析过的fields, methods, types, String。
File:art_method.h
class ArtMethod FINAL {
protected:
...
struct PACKED(4) PtrSizedFields {
// 解释模式时调用此函数的入口
void* entry_point_from_interpreter_;
// 此函数是一个JNI方法时的调用入口
void* entry_point_from_jni_;
// 以本地代码调用时的函数入口
void* entry_point_from_quick_compiled_code_;
}ptr_sized_fields_
...
}
ArtMethod最重要的就是这3个entry_point了,分别表示解释模式时调用此函数的入口,此函数是一个JNI方法时的调用入口,以本地代码调用时的函数入口。
在查找方法时,比如A调用B方法,假设A, B在同一个dex中,经过dex2oat优化后,本地代码将会根据B在DEX中的method_id,索引到DEX_Cache中的resolved_method,然后调用B方法的ArtMethod的entry_point_from_quick_compiled_code指针指向的方法入口。
好,接下来看看DexCache的初始化过程。当classloader load一个尚未加载的类A时,findClass->loadClass->defineClass A的时候,会去ClassLinker(单例)中查找A类所在的DEXFile,然后调用ClassLinker->RegisterDexFile()。
File:dalvik_system_file.cc
static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
jobject cookie) {
...
const std::string descriptor(DotToDescriptor(class_name.c_str()));
const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
for (auto& dex_file : *dex_files) {
const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
if (dex_class_def != nullptr) {
...
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
class_linker->RegisterDexFile(*dex_file);
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash,
class_loader, *dex_file, *dex_class_def);
...
}
}
return nullptr;
}
在ClassLinker->RegisterDexFile()中,如果所在DEX还没有在ClassLinker中创建了对应DEX的dex_cache,将会创建一个dexcache并初始化, 然后注册到ClassLinker中,方便后续查找。
File:class_linker.cc
void ClassLinker::RegisterDexFile(const DexFile& dex_file) {
...
Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(self, dex_file)));
{
...
if (IsDexFileRegisteredLocked(dex_file)) {
return;
}
RegisterDexFileLocked(dex_file, dex_cache);
}
}
下面看看DexCache初始化了些啥?
DexCache为该Dex中的String, type, method, field都分配了访问指针和数组,分配的空间大小来源于Dex文件里面的记录,然后调用dex_cache->init()进行初始化。
File: Class_linker.cc
mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) {
StackHandleScope<6> hs(self);
auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>(
GetClassRoot(kJavaLangDexCache)->AllocObject(self))));
...
auto location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str())));
...
auto strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds())));
...
auto types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds())));
...
auto methods(hs.NewHandle(AllocPointerArray(self, dex_file.NumMethodIds())));
...
auto fields(hs.NewHandle(AllocPointerArray(self, dex_file.NumFieldIds())));
...
dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(),
fields.Get(), image_pointer_size_);
return dex_cache.Get();
init()函数中初始化了String, field, types的访问变量,最最最重要的是初始化了该Dex中所有的ArtMethod,初始化的值是从runtime中拿出来的ResolutionMethod, 就是Runtime method。
File: Dex_cache.cc
void DexCache::Init(const DexFile* dex_file, String* location, ObjectArray<String>* strings,
ObjectArray<Class>* resolved_types, PointerArray* resolved_methods,
PointerArray* resolved_fields, size_t pointer_size) {
SetDexFile(dex_file);
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location);
SetFieldObject<false>(StringsOffset(), strings);
SetFieldObject<false>(ResolvedFieldsOffset(), resolved_fields);
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types);
SetFieldObject<false>(ResolvedMethodsOffset(), resolved_methods);
Runtime* const runtime = Runtime::Current();
if (runtime->HasResolutionMethod()) {
// Initialize the resolve methods array to contain trampolines for resolution.
Fixup(runtime->GetResolutionMethod(), pointer_size);
}
}
void DexCache::Fixup(ArtMethod* trampoline, size_t pointer_size) {
// 将ArtMethod都初始化成Runtime method。
auto* resolved_methods = GetResolvedMethods();
for (size_t i = 0, length = resolved_methods->GetLength(); i < length; i++) {
if (resolved_methods->GetElementPtrSize<ArtMethod*>(i, pointer_size) == nullptr) {
resolved_methods->SetElementPtrSize(i, trampoline, pointer_size);
}
}
}
而Runtime的ResolutionMethod是从oat文件头里面取出来的,也就是说,这个ResulutionMethod是dex2oat写到oat文件里面去的。
File: Image.cc
ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_location,
bool validate_oat_file, std::string* error_msg) {
.....
runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod));
....
}
dex2oat写到文件头里面的ResulutionMethod就是下面这个ARTMethod,它的entry_point_from_quick_compiled_code_被初始化为一段汇编指令DEFINE_FUNCTION art_quick_resolution_trampoline
,这段汇编代码通过宏GetQuickResolutionStub()获取到。其实这段汇编代码最初就是放在boot.oat中的。
File: Runtime.cc
ArtMethod* Runtime::CreateResolutionMethod() {
auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
...
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
return method;
}
辗转,这段汇编代码最终通过一个汇编的跳转Symbol指令跳回到C函数的artQuickResolutionTrampoline()方法中。以下是一段X86 64位汇编,针对每种平台,都会有对应的一个trampoline汇编函数。
File: quick_entrypoints_x86_64.S
DEFINE_FUNCTION art_quick_resolution_trampoline
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
movq %gs:THREAD_SELF_OFFSET, %rdx
movq %rsp, %rcx
call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)
movq %rax, %r10 // Remember returned code pointer in R10.
movq (%rsp), %rdi // Load called method into RDI.
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
testq %r10, %r10 // If code pointer is null goto deliver pending exception.
jz 1f
jmp *%r10 // Tail call into method.
1:
DELIVER_PENDING_EXCEPTION
END_FUNCTION art_quick_resolution_trampoline
阶段小结
一个dexfile中的类如果从来没有被加载过,那么defineClass会为这个dexFile创建一个DexCache,这个DexCache包含一个指针数组resolved_methods_,数组的大小就是当前dexFile中所有的method方法数:
HeapReference<PointerArray> resolved_methods_;
并且初始化所有的指针都指向了一个叫做Runtime Method的ArtMethod,这个Runtime Method就是个“狸猫”,他只是一个代理,后面会将真正的“太子”方法请回来,好,这个Runtime Method也是一个ArtMethod, 它有三个跳转指针变量,其中最重要的entry_point_from_quick_compiled_code_指向一段汇编指令,最后回调到C函数artQuickResolutionTrampoline()
void* entry_point_from_quick_compiled_code_;
好了,如果是同一个dex内部跳转,在类首次被调用时,那么它的函数调用接口最终都走到了这个artQuickResolutionTrampoline().
比如类A的fA()调用到了类B的fB(),A.fA()->B.fB(),dex2oat在生成A.f()的native code时,其调用B.fA()最后就偏移到了Class B所在的dexcache中fB对应的ArtMethod的entry_point_from_quick_compiled_code_地址。dex2oat如何知道fB对应的ArtMethod呢?因为Dex文件里面保存了fB在该Dex中的信息,包括签名和methodId,而dexcache中的ArtMethods数组就是按照这个methodId来对应到各个method的。所以, dex2oat在解析了Dex文件后根据methodId就知道到哪个ArtMethod去调用entry_point_from_quick_compiled_code_了。
下面是一个汇编代码的例子:
函数
com.mogujie.www.hotpatchtest.HotPatchInvoker.<init>()
对应的汇编代码如下,eax+63808就是根据methodId偏移找到对应method在dexcache中的ArtMethod指针,然后通过偏移44找到entry_point_from_quick_compiled_code_地址。
0x003bb482: 8B8040F90000 mov eax, [eax + 63808]
0x003bb488: FF502C call [eax + 44]
请回“太子”
在上面分析完类首次调用的过程后,我们需要看下artQuickResolutionTrampoline()里面的处理,以及各个不同方法的调用流程。
方法调用分为五种指令:
invoke_virtual 调用一个虚方法,即非private, static, final, constructor方法
invoke_super 调用最近的父类的virtual方法, 对应的method_id和calling clsss一致
invoke_direct 调用一个非static的direct方法,也就是private, constructor方法
invoke_static 调用一个static的direct方法
invoke_interface 调用一个interface方法,当操作一个不知道实体类的object时使用,method_id指向的是一个interface。
以下是artQuickResolutionTrampoline()的全部代码体,是整个Runtime的核心代码,对于同一个dex中的相互跳转,以上五种类型的方法调用都会走到这个函数中(dex2oat优化的native代码最终都会调用到函数artQuickResolutionTrampoline),第一个参数called表示被调用的类方法,第二个参数receiver表示被调用的对象,也就是接收消息的对象,第三个参数thread表示当前线程,第四个参数sp指向调用栈顶。
先大致浏览一下artQuickResolutionTrampoline()方法,后面针对5中invoke_方法做独立代码分析。
File: Quick_trampoline_entry.cc
extern "C" const void* artQuickResolutionTrampoline(
ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
...
// 获取被调用方法的一些信息
ClassLinker* linker = Runtime::Current()->GetClassLinker();
ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
InvokeType invoke_type;
MethodReference called_method(nullptr, 0);
// 是否一个“狸猫”Artmethod?如果是,表示方法的真正ArtMethod还没创建好。
const bool called_method_known_on_entry = !called->IsRuntimeMethod();
if (!called_method_known_on_entry) {
uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));
const DexFile::CodeItem* code;
called_method.dex_file = caller->GetDexFile();
code = caller->GetCodeItem();
CHECK_LT(dex_pc, code->insns_size_in_code_units_);
const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
Instruction::Code instr_code = instr->Opcode();
bool is_range;
switch (instr_code) {
case Instruction::INVOKE_DIRECT:
invoke_type = kDirect;
is_range = false;
break;
case Instruction::INVOKE_DIRECT_RANGE:
invoke_type = kDirect;
is_range = true;
break;
case Instruction::INVOKE_STATIC:
invoke_type = kStatic;
is_range = false;
break;
case Instruction::INVOKE_STATIC_RANGE:
invoke_type = kStatic;
is_range = true;
break;
case Instruction::INVOKE_SUPER:
invoke_type = kSuper;
is_range = false;
break;
case Instruction::INVOKE_SUPER_RANGE:
invoke_type = kSuper;
is_range = true;
break;
case Instruction::INVOKE_VIRTUAL:
invoke_type = kVirtual;
is_range = false;
break;
case Instruction::INVOKE_VIRTUAL_RANGE:
invoke_type = kVirtual;
is_range = true;
break;
case Instruction::INVOKE_INTERFACE:
invoke_type = kInterface;
is_range = false;
break;
case Instruction::INVOKE_INTERFACE_RANGE:
invoke_type = kInterface;
is_range = true;
break;
default:
LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);
UNREACHABLE();
}
//从Dex的instr代码中获取dex_method_index。
called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
} else {
//这个地方有点绕,对于Static方法来说,被调用的时候可能所在类尚未被初始化(<clinit>())。这个时候就需要在这里block Static
//方法的调用,直到类初始化完成。但是类初始化可能其它线程在做了,这时候artQuickResolutionTrampoline就会仍然返回“狸
//猫”Artmethod, 这不,又进来了这里了,好吧,继续等待初始化完成。
invoke_type = kStatic;
called_method.dex_file = called->GetDexFile();
called_method.dex_method_index = called->GetDexMethodIndex();
}
....
//是否一个invoke_virtual/invoke_interface方法
const bool virtual_or_interface = invoke_type == kVirtual || invoke_type == kInterface;
// Resolve method filling in dex cache.
if (!called_method_known_on_entry) {
....
//调用class_linker将calledMethod所在的类加载进来,为每个method生成一个ArtMethod,
//并将entry_point_from_quick_compiled_code_设置为native code.
//这里将得到“太子”ArtMethod。
called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);
}
const void* code = nullptr;
if (LIKELY(!self->IsExceptionPending())) {
// 兼容性检查
CHECK(!called->CheckIncompatibleClassChange(invoke_type))
<< PrettyMethod(called) << " " << invoke_type;
if (virtual_or_interface) {
...
//如果是一个invoke_virtual/invoke_interface方法,那么当前找到的方法可能是一个父类的ArtMethod或者一个interface类的
//ArtMethod,这时候就需要从receiver实例中找到receiver的类,并根据vtable/ifTable找到真正需要的ArtMethod。
// vtable/ifTable是存在于Class对象里面的用来查找本类virtual method/interface method的索引表
ArtMethod* orig_called = called;
if (invoke_type == kVirtual) {
called = receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*));
} else {
called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*));
}
...
}
...
// 确保调用的类已经初始化好了
linker->EnsureInitialized(soa.Self(), called_class, true, true);
if (LIKELY(called_class->IsInitialized())) {
...
//从called的ArtMethod中拿出entry_point_from_quick_compiled_code_指针
code = called->GetEntryPointFromQuickCompiledCode();
}
} else if (called_class->IsInitializing()) {
...
else if (invoke_type == kStatic) {
// 类还在初始化,那么继续走trampling吧,咱再等等。
code = linker->GetQuickOatCodeFor(called);
} else {
// 非static方法,直接调用native code。
code = called->GetEntryPointFromQuickCompiledCode();
}
}
}
...
return code;
}
invoke_direct调用流程
分析artQuickResolutionTrampoline()代码,我们先看看invoke_direct xxx调用方法指令的流程。
const bool called_method_known_on_entry = !called->IsRuntimeMethod();
inline bool ArtMethod::IsRuntimeMethod() {
return dex_method_index_ == DexFile::kDexNoIndex;
}
called_method_known_on_entry是一个flag用来标记当前的called变量(ArtMethod类型)是否是一个运行时的ArtMethod,如果是,那么表示这个ArtMethod只是一个“狸猫“ArtMethod,如上分析,他就是DexCache初始化的时候指向的Resolution Method,知道这是一个占位替身后,需要在artQuickResolutionTrampoline()方法中去找到真正的那个ArtMethod。如何找到这个真的ArtMethod呢?首先通过字节码获取到要查找的method一个dex_method_index。
called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
然后在ClassLinker里面去查找/构造这个ArtMethod。ART虚拟机在被调用的方法所在类还没有加载进来时,对所有的dex方法的调用都会用一个替身ArtMethod进行“虚假”调用,在这个“虚假”调用里面,找到真正的ArtMethod后,回填dexcache中的ArtMethod指针, 这样下次native代码就找到真的那个ArtMethod进行调用了。
if (!called_method_known_on_entry) {
...
called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);
}
如下,分析ResolvedMethod()代码,首先在dex_cache中查找,找到则直接返回,当然得满足条件非空且非Runtime method。当然如果类尚未加载,当前dex_cache中还是那个替身ArtMethod,那么resolved为空。好,继续往下,调用ResolveType()函数对类进行解析。
File: Class_liner.cc
ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
ArtMethod* referrer, InvokeType type) {
DCHECK(dex_cache.Get() != nullptr);
// 检查Cache是否有
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
return resolved;
}
// 获取调用类
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
if (klass == nullptr) {
return nullptr;
}
// 先尝试在本dex的dex_cache找,输入参数是method_idx.
switch (type) {
case kDirect: // Fall-through.
case kStatic:
resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_);
break;
...
}
...
// 兼容性检查,返回找到的ArtMethod
if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) {
// Be a good citizen and update the dex cache to speed subsequent calls.
dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);
return resolved;
} else {
.....
}
}
在ResolveType()中,会调用pathClassLoader加载需要的类,这里会对每个类创建一个nativie层的Class对象,Class对象就是java类在虚拟机内存中的一个印象,如下,这个Class对象会保存所在的dex的dex_cache, 接口表(interface table), 父类(super class), 虚函数表(virtual table), direct methods指针, fields指针, virtual methods指针等。这些变量都会在Class_linker的DefineClass()函数里面被赋值。
File: Class.h
class MANAGED Class FINAL : public Object {
// Defining class loader, or null for the "bootstrap" system loader.
HeapReference<ClassLoader> class_loader_;
HeapReference<DexCache> dex_cache_;
HeapReference<IfTable> iftable_;
// The superclass, or null if this is java.lang.Object, an interface or primitive type.
HeapReference<Class> super_class_;
// virtual table, 存储当前类及父类的virtual methods
HeapReference<PointerArray> vtable_;
// static, private, and <init> methods. Pointer to an ArtMethod array.
uint64_t direct_methods_;
// instance fields
uint64_t ifields_;
// Static fields
uint64_t sfields_;
// Virtual methods defined in this class; invoked through vtable. Pointer to an ArtMethod array.
uint64_t virtual_methods_;
};
创建好这个class的native对象以后,这个class对象又会被放入dex_cache中。以后访问这个类的时候就可以直接从dex_cache中拿了。
File: class_linker.cc
mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
......
resolved = FindClass(self, descriptor, class_loader);
if (resolved != nullptr) {
dex_cache->SetResolvedType(type_idx, resolved);
}
}
OK, 看看class对象的赋值过程,流程是这样的:
artQuickResolutionTrampoline()->ResolveMethod()->ResolveType()->FindClass()->DefineClass()->LoadClass()->LoadClassMemebers()
由于代码太多,这里直接跳入LoadClassMemebers()函数, LoadClassMemebers()函数对新创建的class对象进行了赋值,包括:
1. Static fields数组赋值
2. Instant fields数组赋值
3. direct methods的ArtMethod数组赋值
4. virtual methods的ArtMethod数组赋值
File: Class_linker.cc
void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,
const uint8_t* class_data,
Handle<mirror::Class> klass,
const OatFile::OatClass* oat_class) {
{
...
/** 初始化Static fields **/
const size_t num_sfields = it.NumStaticFields();
ArtField* sfields = num_sfields != 0 ? AllocArtFieldArray(self, num_sfields) : nullptr;
for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
LoadField(it, klass, &sfields[i]);
}
klass->SetSFields(sfields);
klass->SetNumStaticFields(num_sfields);
// 加载intant fields **/
const size_t num_ifields = it.NumInstanceFields();
ArtField* ifields = num_ifields != 0 ? AllocArtFieldArray(self, num_ifields) : nullptr;
for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {
LoadField(it, klass, &ifields[i]);
}
klass->SetIFields(ifields);
klass->SetNumInstanceFields(num_ifields);
// 给Direct methods分配空间
if (it.NumDirectMethods() != 0) {
klass->SetDirectMethodsPtr(AllocArtMethodArray(self, it.NumDirectMethods()));
}
klass->SetNumDirectMethods(it.NumDirectMethods());
// 给Virtual methods分配空间
if (it.NumVirtualMethods() != 0) {
klass->SetVirtualMethodsPtr(AllocArtMethodArray(self, it.NumVirtualMethods()));
}
klass->SetNumVirtualMethods(it.NumVirtualMethods());
size_t class_def_method_index = 0;
uint32_t last_dex_method_index = DexFile::kDexNoIndex;
size_t last_class_def_method_index = 0;
// 初始化Direct methods
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);
LoadMethod(self, dex_file, it, klass, method);
LinkCode(method, oat_class, 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++;
}
// 初始化Virtual methods
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
LoadMethod(self, dex_file, it, klass, method);
DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
LinkCode(method, oat_class, class_def_method_index);
class_def_method_index++;
}
}
}
好,终于可以看到“太子”ArtMethod如何创建被回填了,狸猫换太子的游戏可以结束了。
void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,
....
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);
LoadMethod(self, dex_file, it, klass, method);
LinkCode(method, oat_class, class_def_method_index);
先从kclass的native class对象中拿出需要构造的ArtMethod对象,然后调用LoadMethod对其进行赋值,包括method_id, method_name等。
void ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it,
Handle<mirror::Class> klass, ArtMethod* dst) {
uint32_t dex_method_idx = it.GetMemberIndex();
const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);
dst->SetDexMethodIndex(dex_method_idx);
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
然后ClassLoadMembers()调用LinkCode()方法对ArtMethod的entry_point_from_quick_compiled_code_进行赋值。
void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,
uint32_t class_def_method_index) {
...
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(class_def_method_index);
oat_method.LinkMethod(method);
}
...
}
对于invoke_direct方法来说,ArtMethod的entry_point_from_quick_compiled_code_是通过oat_method.LinkMethod(method);
赋值的, 最终被赋值为oat_method的code,也就是这个method在oat文件中的native地址。好了,通过这样回填dexcache中的ArtMethod以后,下次这个method被调用到的时候找到的就是“太子”而不是“狸猫”了。“太子”ArtMethod的entry_point_from_quick_compiled_code_指针指向的是当前method的native code ptr。
File: Oat_file.cc
void OatFile::OatMethod::LinkMethod(ArtMethod* method) const {
CHECK(method != nullptr);
method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
}
File: Oat_file.h
...
class OatMethod FINAL {
...
const void* GetQuickCode() const {
return GetOatPointer<const void*>(code_offset_);
}
我们继续回退到ResolveMethod()中,在创建好“太子”ArtMethod后,将其放入dex_cache中,方便下次调用。
artQuickResolutionTrampoline()->ResolveMethod()->ResolveType()->FindClass()->DefineClass()->LoadClass()->LoadClassMemebers()
File: Class_linker.cc
ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx,
...
// 对类进行兼容性检查
if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) {
// 将dex_cache中的ResolvedMethod(“狸猫”)换成“太子”,下次调用就直接走native code了。
dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);
return resolved;
}
最后到artQuickResolutionTrampoline(),将找到的CalledMethod塞入callee的栈帧中,当作第一个参数,然后返回native code地址。art_quick_resolution_trampoline汇编代码直接调用code:call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)
File: Quick_trampoline_entrypoints.cc
// Lazily resolve a method for quick. Called by stub code.
extern "C" const void* artQuickResolutionTrampoline(
ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)
{
...
if (!called_method_known_on_entry) {
...
// 调用ResolveMethod找到“太子”
called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);
}
...
code = called->GetEntryPointFromQuickCompiledCode();
...
// 将找到的CalledMethod塞入callee的栈帧中,当作第一个参数
*sp = called;
// 返回native code
return code
}
File: quick_entrypoints_x86_64.S
DEFINE_FUNCTION art_quick_resolution_trampoline
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
movq %gs:THREAD_SELF_OFFSET, %rdx
movq %rsp, %rcx
call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)
movq %rax, %r10 // Remember returned code pointer in R10.
movq (%rsp), %rdi // Load called method into RDI.
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
testq %r10, %r10 // If code pointer is null goto deliver pending exception.
jz 1f
jmp *%r10 // Tail call into method.
1:
DELIVER_PENDING_EXCEPTION
END_FUNCTION art_quick_resolution_trampoline
invoke_static调用流程
基本类似invoke_direct方法,invoke_static方法稍微有点特殊,就是在调用static方法时,其所在的类可能尚未被初始化(\
File: Quick_trampoline_entrypoints.cc
// Lazily resolve a method for quick. Called by stub code.
extern "C" const void* artQuickResolutionTrampoline(
ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
...
const bool called_method_known_on_entry = !called->IsRuntimeMethod();
if (!called_method_known_on_entry) {
uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));
const DexFile::CodeItem* code;
called_method.dex_file = caller->GetDexFile();
code = caller->GetCodeItem();
const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
Instruction::Code instr_code = instr->Opcode();
bool is_range;
switch (instr_code) {
...
case Instruction::INVOKE_STATIC:
invoke_type = kStatic;
is_range = false;
break;
case Instruction::INVOKE_STATIC_RANGE:
invoke_type = kStatic;
is_range = true;
break;
}
called_method.dex_method_index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
} else {
/*
** 上次调用EnsureInitialized()时,另外一个线程正在初始化,且未初始化好,
** 继续调用artQuickResolutionTrampoline
*/
invoke_type = kStatic;
called_method.dex_file = called->GetDexFile();
called_method.dex_method_index = called->GetDexMethodIndex();
}
....
const bool virtual_or_interface = invoke_type == kVirtual || invoke_type == kInterface;
// Resolve method filling in dex cache.
if (!called_method_known_on_entry) {
...
/* 调用ResolveMethod来解析Class并创建ArtMethod */
called = linker->ResolveMethod(self, called_method.dex_method_index, caller, invoke_type);
}
const void* code = nullptr;
if (LIKELY(!self->IsExceptionPending())) {
...
if (virtual_or_interface) {
...
} else if (invoke_type == kStatic) {
const auto called_dex_method_idx = called->GetDexMethodIndex();
// For static invokes, we may dispatch to the static method in the superclass but resolve
// using the subclass. To prevent getting slow paths on each invoke, we force set the
// resolved method for the super class dex method index if we are in the same dex file.
// b/19175856
if (called->GetDexFile() == called_method.dex_file &&
called_method.dex_method_index != called_dex_method_idx) {
called->GetDexCache()->SetResolvedMethod(called_dex_method_idx, called, sizeof(void*));
}
}
/*
** 确认类初始化成功,static方法的ArtMethod的art_quick_resolution_trampoline
** 变量赋值也在EnsureInitialized()在类初始化好之后赋值为native code的
*/
...
linker->EnsureInitialized(soa.Self(), called_class, true, true);
if (LIKELY(called_class->IsInitialized())) {
if (UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self, called))) {
...
} else {
code = called->GetEntryPointFromQuickCompiledCode();
}
} else if (called_class->IsInitializing()) {
if (UNLIKELY(Dbg::IsForcedInterpreterNeededForResolution(self, called))) {
...
} else if (invoke_type == kStatic) {
// 类还在初始化中,那么继续调用artQuickResolutionTrampoline方法。
code = linker->GetQuickOatCodeFor(called);
} else {
// No trampoline for non-static methods.
code = called->GetEntryPointFromQuickCompiledCode();
}
} else {
DCHECK(called_class->IsErroneous());
}
}
// 将找到的CalledMethod塞入callee的栈帧中,当作第一个参数
*sp = called;
return code;
}
invoke_virtual调用
Class类中vtable_和virtual_methods_用来访问类中的virtual methods。
File: Class.h
class MANAGED Class FINAL : public Object {
...
// Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass is
// copied in, and virtual methods from our class either replace those from the super or are
// appended. For abstract classes, methods may be created in the vtable that aren't in
// virtual_ methods_ for miranda methods.
HeapReference<PointerArray> vtable_;
// Virtual methods defined in this class; invoked through vtable. Pointer to an ArtMethod array.
uint64_t virtual_methods_;
...
}
同样类似invoke_direct,只是在找到ArtMethod后,这个ArtMethod可能是父类的,之后需要从实例(receiver)中获取到实例的类(可能是一个子类,也可能就是父类本身),然后通过一个FindVirtualMethodForVirtual方法到vtable(virtual table)中找到实例类的ArtMethod。
// Lazily resolve a method for quick. Called by stub code.
extern "C" const void* artQuickResolutionTrampoline(
ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
...
const void* code = nullptr;
if (LIKELY(!self->IsExceptionPending())) {
if (virtual_or_interface) {
/*
** dex2oat一个virtual方法时,当时指向的类可能是super类,这时候就需要找到实例receiver,获取其
** 真正的sub类,然后通过FindVirtualMethodForVirtual找到sub类的实现,对于interfacel类似。
*/
ArtMethod* orig_called = called;
if (invoke_type == kVirtual) {
called = receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*));
} else {
called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*));
}
...
}
// Ensure that the called method's class is initialized.
linker->EnsureInitialized(soa.Self(), called_class, true, true);
if (LIKELY(called_class->IsInitialized())) {
...
//获取native code的入口地址
code = called->GetEntryPointFromQuickCompiledCode();
}
}
...
return code;
}
我们来看下下面这个代码片段的例子,ExtendsCase3Sub是ExtendsCase3Super的一个子类。dex2oat在优化testExtendsCase3()的代码时,并不知道extends的实际类型,所以只能到ExtendsCase3Super类中查找对应的ArtMethod。
void main(){
....
ExtendsCase3SuperPatch extends3Sub = new ExtendsCase3Sub();
ExtendsCase3SuperPatch extends3Super = new ExtendsCase3Super();
result = testExtendsCase3(extends3Sub);
result = testExtendsCase3(extends3Super);
}
public boolean testExtendsCase3(ExtendsCase3SuperPatch extends3) throws IOException {
return extends3.method2();
}
testExtendsCase3()方法对应的Dex代码如下,代码里找super类,即ExtendsCase3Super.method2()。
boolean com.mogujie.www.hotpatchtest.cases.CasesTest.testExtendsCase3(com.mogujie.www.hotpatchtest.cases.Extends.ExtendsCase3Super) (dex_method_idx=15974)
DEX CODE:
0x0000: invoke-virtual {v2}, boolean com.mogujie.www.hotpatchtest.cases.Extends.ExtendsCase3Super.method2() // method@15985
0x0003: move-result v0
0x0004: return v0
好了,我们看看FindVirtualMethodForVirtual()
的实现, receiver->GetClass()获取到实例的Class,然后调用FindVirtualMethodForVirtual()通过Super类的method_index_到当前类的vtable中找到对应的ArtMethod。子类如果覆盖了父类的一个方法,那么其method_index_在父类和子类中是一样的。method_index_是在vtable中的一个偏移。
File: Art_method.h
class ArtMethod FINAL {
...
// Entry within a dispatch table for this method. For static/direct methods the index is into
// the declaringClass.directMethods, for virtual methods the vtable and for interface methods the
// ifTable.
uint32_t method_index_;
}
// receiver->GetClass()获得实例的Class
called = receiver->GetClass()->FindVirtualMethodForVirtual(called, sizeof(void*));
inline ArtMethod* Class::FindVirtualMethodForVirtual(ArtMethod* method, size_t pointer_size) { // The argument method may from a super class.
// Use the index to a potentially overridden one for this instance's class.
return GetVTableEntry(method->GetMethodIndex(), pointer_size);
}
inline ArtMethod* Class::GetVTableEntry(uint32_t i, size_t pointer_size) {
if (ShouldHaveEmbeddedImtAndVTable()) {
return GetEmbeddedVTableEntry(i, pointer_size);
}
auto* vtable = GetVTable();
//从实例的类的vtable中找到实例类中该方法的ArtMethod,i就是method_index_
return vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
}
那么Art虚拟机是如何确保子类覆盖父类的方法后,其method_index_是一样的呢?秘密在LinkVirtualMethods()里,DefineClass()的时候,会为当前被define的类生成一个vtable(vtable是一个用来保存当前类的所有virtual方法的Artmethod的一个指针数组),然后找到其直接父类。
1. 将直接父类的vtable拷贝到当前类的vtable中。
2. 查找vtable中是否有跟当前类一样签名的方法,如果有,那么用当前类的artmethod直接覆盖vtable中的父类的artmethod
3. 将存在于当前类的virtual method而不存在于父类的append到vtable尾部。
ResolveMethod()->ResolveType()->FindClass()->FindClassInPathClassLoader()->DefineClass()->LinkClass()->LinkMethods()->LinkVirtualMethods()
File: Class_linker.cc
bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) {
const size_t num_virtual_methods = klass->NumVirtualMethods();
if (klass->HasSuperClass()) {
const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength();
const size_t max_count = num_virtual_methods + super_vtable_length;
...
MutableHandle<mirror::PointerArray> vtable;
if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
/*
** 父类是一个abstract类,那么vtable已经嵌入到了父类的字节码中。
*/
vtable = hs.NewHandle(AllocPointerArray(self, max_count));
for (size_t i = 0; i < super_vtable_length; i++) {
vtable->SetElementPtrSize(
i, super_class->GetEmbeddedVTableEntry(i, image_pointer_size_), image_pointer_size_);
}
if (num_virtual_methods == 0) {
klass->SetVTable(vtable.Get());
return true;
}
} else {
/*
** 父类在被define的时候创建了vtable,拷贝父类的vtable到当前类的vtable中。
*/
auto* super_vtable = super_class->GetVTable();
if (num_virtual_methods == 0) {
klass->SetVTable(super_vtable);
return true;
}
vtable = hs.NewHandle(down_cast<mirror::PointerArray*>(
super_vtable->CopyOf(self, max_count)));
}
// How the algorithm works:
// 1. Populate hash table by adding num_virtual_methods from klass. The values in the hash
// table are: invalid_index for unused slots, index super_vtable_length + i for a virtual
// method which has not been matched to a vtable method, and j if the virtual method at the
// index overrode the super virtual method at index j.
// 2. Loop through super virtual methods, if they overwrite, update hash table to j
// (j < super_vtable_length) to avoid redundant checks. (TODO maybe use this info for reducing
// the need for the initial vtable which we later shrink back down).
// 3. Add non overridden methods to the end of the vtable.
static constexpr size_t kMaxStackHash = 250;
const size_t hash_table_size = num_virtual_methods * 3;
uint32_t* hash_table_ptr;
std::unique_ptr<uint32_t[]> hash_heap_storage;
if (hash_table_size <= kMaxStackHash) {
hash_table_ptr = reinterpret_cast<uint32_t*>(
alloca(hash_table_size * sizeof(*hash_table_ptr)));
}
LinkVirtualHashTable hash_table(klass, hash_table_size, hash_table_ptr, image_pointer_size_);
// Add virtual methods to the hash table.
for (size_t i = 0; i < num_virtual_methods; ++i) {
hash_table.Add(i);
}
// Loop through each super vtable method and see if they are overriden by a method we added to
// the hash table.
for (size_t j = 0; j < super_vtable_length; ++j) {
// Search the hash table to see if we are overidden by any method.
ArtMethod* super_method = vtable->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
MethodNameAndSignatureComparator super_method_name_comparator(
super_method->GetInterfaceMethodIfProxy(image_pointer_size_));
uint32_t hash_index = hash_table.FindAndRemove(&super_method_name_comparator);
if (hash_index != hash_table.GetNotFoundIndex()) {
ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(
hash_index, image_pointer_size_);
if (klass->CanAccessMember(super_method->GetDeclaringClass(),
super_method->GetAccessFlags())) {
vtable->SetElementPtrSize(j, virtual_method, image_pointer_size_);
virtual_method->SetMethodIndex(j);
}
}
}
// Add the non overridden methods at the end.
size_t actual_count = super_vtable_length;
for (size_t i = 0; i < num_virtual_methods; ++i) {
ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);
size_t method_idx = local_method->GetMethodIndexDuringLinking();
if (method_idx < super_vtable_length &&
local_method == vtable->GetElementPtrSize<ArtMethod*>(method_idx, image_pointer_size_)) {
continue;
}
vtable->SetElementPtrSize(actual_count, local_method, image_pointer_size_);
local_method->SetMethodIndex(actual_count);
++actual_count;
}
// Shrink vtable if possible
if (actual_count < max_count) {
vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, actual_count)));
}
klass->SetVTable(vtable.Get());
} else {
/*
** 没有父类,那么将当前类的所有virtual methods的Artmethods加进vtable。
*/
auto* vtable = AllocPointerArray(self, num_virtual_methods);
for (size_t i = 0; i < num_virtual_methods; ++i) {
ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);
vtable->SetElementPtrSize(i, virtual_method, image_pointer_size_);
virtual_method->SetMethodIndex(i & 0xFFFF);
}
klass->SetVTable(vtable);
}
return true;
}
invoke_super 调用过程
跟invoke_direct流程一样,其实也就是访问另外一个类的方法,只不过这个类有点特殊,是一个自己的super类而已。
invoke_interface 调用过程
类似invoke_virtual,interface接口是通过iftable_来访问。ifTable是一个指针数组,指向implements的各个接口类。
File: Class.h
class MANAGED Class FINAL : public Object {
...
HeapReference<IfTable> iftable_;
...
}
同样类似invoke_virutal, 调用invoke_interface的时候,刚开始找到的called(ArtMethod类型)是隶属于interface类的,仍然需要调用receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*))
来获取receiver实例的那个类的ArtMethod。
// Lazily resolve a method for quick. Called by stub code.
extern "C" const void* artQuickResolutionTrampoline(
ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
...
called = receiver->GetClass()->FindVirtualMethodForInterface(called, sizeof(void*));
code = called->GetEntryPointFromQuickCompiledCode();
...
return code;
}
来看看FindVirtualMethodForInterface()的实现,可以看到每一个Class都有一个interface table数组,由于一个类可以implement多个interface接口类,每个接口类在这个interface table中都有一个MethodArray()。
inline ArtMethod* Class::FindVirtualMethodForInterface(ArtMethod* method, size_t pointer_size) {
Class* declaring_class = method->GetDeclaringClass();
const int32_t iftable_count = GetIfTableCount();
IfTable* iftable = GetIfTable();
for (int32_t i = 0; i < iftable_count; i++) {
//找到interface table中的接口类的方法数组
if (iftable->GetInterface(i) == declaring_class) {
//从方法数组中找到需要的ArtMethod
return iftable->GetMethodArray(i)->GetElementPtrSize<ArtMethod*>(
method->GetMethodIndex(), pointer_size);
}
}
return nullptr;
}
再看看interface table数组是如何被赋值的?调用过程类似virtual table的生成。
ResolveMethod()->ResolveType()->FindClass()->FindClassInPathClassLoader()->DefineClass()->LinkClass()->LinkMethods()-> LinkInterfaceMethods()
相比invoke_virtual, invoke_interface相对复杂很多,因为接口类可以实现另外一个接口类,同时还能继承其它的super类。大致流程如下:
1. 计算总的接口类数量,如果总的数量为0则直接返回
2. 如果当前类没有实现接口类,且父类只是实现了marker接口类(即serializalbe, clonable),则直接复制父类的ifTable,返回
3. 将父类的ifTable拷贝到当前类的ifTable。
4. 平铺ifTable,就是将直接接口类append到ifTable,如果有重复则过滤,然后再将直接接口类的接口类依次放入ifTable中。
5. 从vTable找真正实现的Artmthod: 从当前类的vTable中找跟ifTable签名一样的方法,如果有,则将vTable中的ArtMethod放入该ifTable中。这样,从ifTable中找方法时,就可以直接找到需要的ArtMethod了。
具体见以下代码分析:
bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass,
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
ArtMethod** out_imt) {
...
const bool has_superclass = klass->HasSuperClass();
//父类可能也实现了接口类
const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
//当前类是否实现了接口类
const bool have_interfaces = interfaces.Get() != nullptr;
const size_t num_interfaces =
have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
const size_t method_size = ArtMethod::ObjectSize(image_pointer_size_);
if (num_interfaces == 0) {
if (super_ifcount == 0) {
//当前类和父类没有实现接口则直接返回
return true;
}
// 当前类没有实现接口,父类有实现的接口类的情况下,has_non_marker_interface
// 这个标记位检查是否有非marker interface(如serializable, clonable等)的接口。
bool has_non_marker_interface = false;
mirror::IfTable* super_iftable = klass->GetSuperClass()->GetIfTable();
for (size_t i = 0; i < super_ifcount; ++i) {
if (super_iftable->GetMethodArrayCount(i) > 0) {
has_non_marker_interface = true;
break;
}
}
// 父类有marker interface(如serializable, clonable等), 那么直接用父类的interface table即可
if (!has_non_marker_interface) {
klass->SetIfTable(super_iftable);
return true;
}
}
// 计算接口类的数量。
size_t ifcount = super_ifcount + num_interfaces;
for (size_t i = 0; i < num_interfaces; i++) {
mirror::Class* interface = have_interfaces ?
interfaces->GetWithoutChecks(i) : mirror::Class::GetDirectInterface(self, klass, i);
...
ifcount += interface->GetIfTableCount();
}
MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
//拷贝父类的接口类到当前类的iftable中
if (super_ifcount != 0) {
mirror::IfTable* super_iftable = klass->GetSuperClass()->GetIfTable();
for (size_t i = 0; i < super_ifcount; i++) {
mirror::Class* super_interface = super_iftable->GetInterface(i);
iftable->SetInterface(i, super_interface);
}
}
// 平铺iftable
size_t idx = super_ifcount;
for (size_t i = 0; i < num_interfaces; i++) {
mirror::Class* interface = have_interfaces ? interfaces->Get(i) :
mirror::Class::GetDirectInterface(self, klass, i);
// 检查下当前类直接继承的接口类是否已经放入iftable中
bool duplicate = false;
for (size_t j = 0; j < idx; j++) {
mirror::Class* existing_interface = iftable->GetInterface(j);
if (existing_interface == interface) {
duplicate = true;
break;
}
}
// 还没放置的话直接append到后面,否则continue..
if (!duplicate) {
// 将interface类加到iftable后面
iftable->SetInterface(idx++, interface);
// 直接接口类可能继承了其它接口类,那么将其它接口类添加到iftable后面
for (int32_t j = 0; j < interface->GetIfTableCount(); j++) {
mirror::Class* super_interface = interface->GetIfTable()->GetInterface(j);
bool super_duplicate = false;
for (size_t k = 0; k < idx; k++) {
mirror::Class* existing_interface = iftable->GetInterface(k);
if (existing_interface == super_interface) {
super_duplicate = true;
break;
}
}
if (!super_duplicate) {
iftable->SetInterface(idx++, super_interface);
}
}
}
}
...
// 压缩iftable,因为可能会有dupliate接口类,也就是前面申请的内存多了,现在根据实际情况释放一些。
if (idx < ifcount) {
iftable.Assign(down_cast<mirror::IfTable*>(
iftable->CopyOf(self, idx * mirror::IfTable::kMax)));
ifcount = idx;
}
klass->SetIfTable(iftable.Get());
// 接下来代码处理virtual table,如何当前类就是一个接口类,那么它不需要virtual table的函数指针,直接返回
if (klass->IsInterface()) {
return true;
}
...
MutableHandle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
ArtMethod* const conflict_method = runtime->GetImtConflictMethod();
bool extend_super_iftable = false;
...
// 为iftable分配内存
for (size_t i = 0; i < ifcount; ++i) {
size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
if (num_methods > 0) {
const bool is_super = i < super_ifcount;
const bool super_interface = is_super && extend_super_iftable;
mirror::PointerArray* method_array;
if (super_interface) {
mirror::IfTable* if_table = klass->GetSuperClass()->GetIfTable();
// 如果当前if_table数组元素指向的是一个super类的接口类,那么直接扩展这个元素,深拷贝。
method_array = down_cast<mirror::PointerArray*>(if_table->GetMethodArray(i)->Clone(self));
} else {
//当前类直接implements的接口类,那么申请num_methods个ArtMethod指针即可。
method_array = AllocPointerArray(self, num_methods);
}
iftable->SetMethodArray(i, method_array);
}
}
...
// 以下代码去填充iftable中的各个ArtMethod元素,当然是从vtable里面找咯,找到了就填进去。
for (size_t i = 0; i < ifcount; ++i) {
size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
if (num_methods > 0) {
StackHandleScope<2> hs2(self);
const bool is_super = i < super_ifcount;
const bool super_interface = is_super && extend_super_iftable;
auto method_array(hs2.NewHandle(iftable->GetMethodArray(i)));
ArtMethod* input_virtual_methods = nullptr;
Handle<mirror::PointerArray> input_vtable_array = NullHandle<mirror::PointerArray>();
int32_t input_array_length = 0;
if (super_interface) {
// 如果当前interface方法是父类的方法,那么就说明我们正在覆盖父类的一个方法,这时候只需要
// 把当前类的Virtual methods拿出来做输入
input_virtual_methods = klass->GetVirtualMethodsPtr();
input_array_length = klass->NumVirtualMethods();
} else {
// 当前类直接implement的一个接口类方法,这时候就需要把整个vtable都拿出来做输入
// 因为这个接口类的方法可能存在于任何一个super类中
input_vtable_array = vtable;
input_array_length = input_vtable_array->GetLength();
}
if (input_array_length == 0) {
// 没有输入的virtual methods,没必要往下走了,直接continue..
continue;
}
for (size_t j = 0; j < num_methods; ++j) {
auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(
j, image_pointer_size_);
MethodNameAndSignatureComparator interface_name_comparator(
interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));
int32_t k;
// 现在从输入的input_vtable_array或者input_virtual_methods去找if_table
// 中匹配到的方法,如果找到就覆盖
for (k = input_array_length - 1; k >= 0; --k) {
ArtMethod* vtable_method = input_virtual_methods != nullptr ?
reinterpret_cast<ArtMethod*>(
reinterpret_cast<uintptr_t>(input_virtual_methods) + method_size * k) :
input_vtable_array->GetElementPtrSize<ArtMethod*>(k, image_pointer_size_);
ArtMethod* vtable_method_for_name_comparison =
vtable_method->GetInterfaceMethodIfProxy(image_pointer_size_);
if (interface_name_comparator.HasSameNameAndSignature(
vtable_method_for_name_comparison)) {
if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
// 如果找到的匹配的方法非abstract且不是public那么就抛异常
self->EndAssertNoThreadSuspension(old_cause);
ThrowIllegalAccessError(klass.Get(),
"Method '%s' implementing interface method '%s' is not public",
PrettyMethod(vtable_method).c_str(), PrettyMethod(interface_method).c_str());
return false;
}
method_array->SetElementPtrSize(j, vtable_method, image_pointer_size_);
break;
}
}
...
}
}
}
...
return true;
}
Field 访问过程
- const变量, dex2oat会在代码优化中写死它的值,而不会去class中找一遍。
- instant&static变量,dex2oat会在代码优化过程中将instant变量的访问以其类中的偏移来访问,并写到native code中。如:
public boolean testVariable() {
mFieldCase1InvokeePatch = new FieldCase1InvokeePatch();
return mFieldCase1InvokeePatch.flag_var;
}
public class FieldCase1InvokeePatch {
public boolean flag_var = false;
}
以下是testVariable()被dex2oat优化后对应的native代码,8是flag_var在类FieldCase1InvokeePatch的偏移
0x003c187c: 83EC1C sub esp, 28
0x003c187f: 896C2414 mov [esp + 20], ebp
0x003c1883: 89742418 mov [esp + 24], esi
0x003c1887: 890424 mov [esp], eax
0x003c188a: 8BF1 mov esi, ecx
0x003c188c: 8B6E08 mov ebp, [esi + 8]
0x003c188f: 8B6D0C mov ebp, [ebp + 8] //flag_var
JNI方法加载过程
在LinkCode()的时候,如果发现method是一个native method,那么就调用UnregisterNative()。
File: Class_liner.cc
void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,
uint32_t class_def_method_index) {
...
if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative();
}
展开UnregisterNative(). 最后发现ArtMethod的entry_point_from_jni_入口被设置成了art_jni_dlsym_lookup_stub的汇编FUNCTION,所以首次调用这个JNI方法时会走到art_jni_dlsym_lookup_stub中。这个汇编代码会调用artFindNativeMethod()的C方法。
File: Art_method.cc
void ArtMethod::UnregisterNative() {
// restore stub to lookup native pointer via dlsym
RegisterNative(GetJniDlsymLookupStub(), false);
}
void ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
...
SetEntryPointFromJni(native_method);
}
File: Runtime_asm_entrypoints.h
extern "C" void* art_jni_dlsym_lookup_stub(JNIEnv*, jobject);
static inline const void* GetJniDlsymLookupStub() {
return reinterpret_cast<const void*>(art_jni_dlsym_lookup_stub);
}
File: jni_entrypoints_x86.S
DEFINE_FUNCTION art_jni_dlsym_lookup_stub
subl LITERAL(8), %esp // align stack
CFI_ADJUST_CFA_OFFSET(8)
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
call SYMBOL(artFindNativeMethod) // (Thread*)
addl LITERAL(12), %esp // remove argument & padding
CFI_ADJUST_CFA_OFFSET(-12)
testl %eax, %eax // check if returned method code is null
jz .Lno_native_code_found // if null, jump to return to handle
jmp *%eax // otherwise, tail call to intended method
.Lno_native_code_found:
ret
END_FUNCTION art_jni_dlsym_lookup_stub
在artFindNativeMethod()方法中,先通过全局的Vm线程找到对应的method的native 地址,然后将其注册到Jni method的ArtMethod中,也就是将ArtMethod的entry_point_from_jni_入口设置成native代码,下次就直接跳转JNI方法的native代码了。
File: Jni_entrypoints.cc
extern "C" void* artFindNativeMethod() {
...
Thread* self = Thread::Current();
ArtMethod* method = self->GetCurrentMethod(nullptr);
// Lookup symbol address for method, on failure we'll return null with an exception set,
// otherwise we return the address of the method we found.
void* native_code = soa.Vm()->FindCodeForNativeMethod(method);
...
// 注册一下,以后jni跳转就不用再走一遍artFindNativeMethod了,直接跳转native代码
method->RegisterNative(native_code, false);
return native_code;
}
}
接下来看看FindCodeForNativeMethod()如何工作的,原来是通过一个libraries_来查找到native 代码的,这个libraries_是一个记录了所有SharedLibrary的一个map,每个SharedLibrary是一个so在内存中的一个映射,最后轮训所有的SharedLibrary的dlsym()函数将so中的JNI方法load到内存中,并返回其指针。而每次调用LoadNativeLibrary()来注册so时都会在libraries_中新增一个SharedLibrary。
File: Java_vm_ext.cc
void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m) {
...
native_method = libraries_->FindNativeMethod(m, detail);
...
return native_method;
}
/** Libraries 包含了当前VM中所有load过的so **/
class Libraries {
...
private:
AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibraries> libraries_;
};
/** 最终FindSymbol查找JNI func都会走dlsym **/
class SharedLibrary {
void* FindSymbol(const std::string& symbol_name) {
return dlsym(handle_, symbol_name.c_str());
}
};
/** 每次调用LoadNativeLibrary()都会在libraries_中注册一个SharedLibrary。**/
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader,
std::string* error_msg) {
...
library = libraries_->Get(path);
if (library == nullptr) { // We won race to get libraries_lock.
library = new_library.release();
libraries_->Put(path, library);
created_library = true;
}
...
}
反射调用
没怎么看懂,有时间再看吧。写了个demo,跨dex间反射调用field和method都没有问题。
跨dex的method访问
对于跨dex的方法访问,5种方法调用类型都对应一个”跳板”函数。
- invoke_static: artInvokeStaticTrampolineWithAccessCheck
- invoke_direct: artInvokeDirectTrampolineWithAccessCheck
- invoke_super: artInvokeSuperTrampolineWithAccessCheck
- invoke_virtual: artInvokeVirtualTrampolineWithAccessCheck
- invoke_interface: artInvokeInterfaceTrampolineWithAccessCheck
以下是一个代码片段例子,
public boolean invokeStatic2(){
return !AcrossDexCase1StaticMethod.invokeStatic2();
}
invokeStatic2()方法被dex2oat优化的时候生成的native code如下,15892是invokeStatic2()在caller方法所在的dex中的一个methodId,也就是说即使AcrossDexCase1StaticMethod类在另外一个dex中,它的方法在caller dex的dex_cache中也是有一个ArtMethod指针的。
0x0039b787: B8143E0000 mov eax, 15892
0x0039b78c: 64FF1524020000 call fs:[0x224] ; pInvokeStaticTrampolineWithAccessCheck
以invoke_static为例, 看看artInvokeStaticTrampolineWithAccessCheck方法的实现:
extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck(
uint32_t method_idx, mirror::Object* this_object,
ArtMethod* caller_method, Thread* self, ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method,
self, sp);
}
最终以上5个“跳板”函数都会走到artInvokeCommon中,method_idx是native 代码中写死的(例子中的15892),表示其在当前caller的dex_cache中的method偏移,this_object是被操作的对象,caller_method是调用方法(例子中的invokeStatic2), self是虚拟机线程,sp是栈指针。大致流程如下:
1. 尝试通过调用方法FindMethodFast来快速找到method_idx对应的ArtMethod, 找到则直接返回这个ArtMethod的native_code,否则进入2.
2. 调用FindMethodFromCode()继续查找。
template<InvokeType type, bool access_check>
static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
ArtMethod* caller_method, Thread* self, ArtMethod** sp) {
...
ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type);
if (UNLIKELY(method == nullptr)) {
...
{
...
method = FindMethodFromCode<type, access_check>(method_idx, &this_object, &caller_method,
self);
}
}
const void* code = method->GetEntryPointFromQuickCompiledCode();
...
}
首次调用method时FindMethodFast()并不能在dex_cache中命中ArtMethod。那么将走到FindMethodFromCode()中,可以看到最后也是通过调用class_linker的ResolveMethod来将ArtMethod加载到Class和dex_cache中的。后面的逻辑就是处理各种类型的method调用,上面已经介绍过很多,这里就不赘述了。
template<InvokeType type, bool access_check>
inline ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object** this_object,
ArtMethod** referrer, Thread* self) {
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, *referrer);
if (resolved_method == nullptr) {
...
resolved_method = class_linker->ResolveMethod(self, method_idx, *referrer, type);
}
...
switch (type) {
case kStatic:
case kDirect:
return resolved_method;
case kVirtual: {
mirror::Class* klass = (*this_object)->GetClass();
uint16_t vtable_index = resolved_method->GetMethodIndex();
...
return klass->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize());
}
case kSuper: {
mirror::Class* super_class = (*referrer)->GetDeclaringClass()->GetSuperClass();
uint16_t vtable_index = resolved_method->GetMethodIndex();
...
return super_class->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize());
}
case kInterface: {
uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize;
ArtMethod* imt_method = (*this_object)->GetClass()->GetEmbeddedImTableEntry(
imt_index, class_linker->GetImagePointerSize());
if (!imt_method->IsImtConflictMethod() && !imt_method->IsImtUnimplementedMethod()) {
...
return imt_method;
} else {
ArtMethod* interface_method = (*this_object)->GetClass()->FindVirtualMethodForInterface(
resolved_method, class_linker->GetImagePointerSize());
...
return interface_method;
}
}
...
}
}
以下是ResolveMethod的逻辑:
- 先走Caller所在dex的dex_cache找
- 尝试使用method_idx从load到的target method所在的Class中查找,这个地方有点tricky,
klass->FindxxxMethod
在看到Class所在的dex和caller 方法所在的dex不一致时,就直接返回空了。 - 所以使用方法签名来找,这个应该可以找到,除非这个Class里面没有定义此方法。
- 将找到的ArtMethod存入caller所在dex的dex_cache中,方便下次查找。
ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
ArtMethod* referrer, InvokeType type) {
DCHECK(dex_cache.Get() != nullptr);
// Cache是否命中?
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
...
return resolved;
}
// Cache中没有,那么从method所在的Class中找
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
...
// 从Caller所在的dex的dex_cache中尝试去找这个ArtMethod,如果是跨dex,那么返回就是空。
switch (type) {
case kDirect: // Fall-through.
case kStatic:
resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_);
break;
case kInterface:
resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_);
break;
case kSuper: // Fall-through.
case kVirtual:
resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_);
break;
...
}
if (resolved == nullptr) {
// 只能使用签名从load进来的Class中查找了,肯定能命中,除非这个Class没有定义要找的method。
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
const Signature signature = dex_file.GetMethodSignature(method_id);
switch (type) {
case kDirect: // Fall-through.
case kStatic:
resolved = klass->FindDirectMethod(name, signature, image_pointer_size_);
break;
case kInterface:
resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
break;
case kSuper: // Fall-through.
case kVirtual:
resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
break;
}
}
// 找到方法,那么检查兼容性
if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) {
// 存入Caller所在的dex的dex\_cache中
dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);
return resolved;
}
...
}
ArtMethod* Class::FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx,
size_t pointer_size) {
//Class所在的dex和caller 方法所在的dex不一致时,直接返回空
if (GetDexCache() == dex_cache) {
for (auto& method : GetVirtualMethods(pointer_size)) {
...
if (method.GetDexMethodIndex() == dex_method_idx && !method.IsMiranda()) {
return &method;
}
}
}
return nullptr;
}
可以看到,跨dex访问时方法的查找是通过签名找到的。
跨dex的Field访问
- const变量, dex2oat会在代码优化中写死它的值,而不会去class中找一遍。
- instance&static变量,不同于同dex的访问,跨dex访问非const变量是走的“quick_entry” points接口。
比如下面的访问:
AcrossDexCase2Fields mAcrossDexCase2Fields = new AcrossDexCase2Fields();
public boolean accessStaticBoolean(){
return mAcrossDexCase2Fields.S_NUM;
}
public class AcrossDexCase2Fields {
public static boolean S_NUM = true;
...
}
mAcrossDexCase2Fields.S_NUM;
汇编dex2oat编译为以下native code, 4790是S_NUM的field_idx。之后调用pGet32Static方法。
0x0039bb85: B86A130000 mov eax, 4970
0x0039bb8a: 64FF1570010000 call fs:[0x170] ; pGet32Static
之后调用到artGet32StaticFromCode这个绷床函数,也就是说对field的跨dex访问,ART虚拟机也会在caller所在的dex_cache中为其分配一个ARTField。FindFieldFromCode()函数里面的逻辑这里就不贴了,跟跨dex的method访问逻辑几乎一样,就是当发现field所在类的dex和caller的dex不一致时,直接用field的名字和class的名字进行查找。找到以后填入caller的ARTField中。
extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx,
ArtMethod* referrer,
Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
...
ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int32_t));
if (LIKELY(field != nullptr)) {
return field->Get32(field->GetDeclaringClass());
}
field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int32_t));
if (LIKELY(field != nullptr)) {
return field->Get32(field->GetDeclaringClass());
}
return 0; // Will throw exception by checking with Thread::Current.
}
这样,ART虚拟机的field/method的访问流程已经分析完毕。
更多推荐










所有评论(0)