ART运行时之method/field加载

先简单介绍下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 访问过程

  1. const变量, dex2oat会在代码优化中写死它的值,而不会去class中找一遍。
  2. 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种方法调用类型都对应一个”跳板”函数。

  1. invoke_static: artInvokeStaticTrampolineWithAccessCheck
  2. invoke_direct: artInvokeDirectTrampolineWithAccessCheck
  3. invoke_super: artInvokeSuperTrampolineWithAccessCheck
  4. invoke_virtual: artInvokeVirtualTrampolineWithAccessCheck
  5. 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的逻辑:

  1. 先走Caller所在dex的dex_cache找
  2. 尝试使用method_idx从load到的target method所在的Class中查找,这个地方有点tricky,klass->FindxxxMethod在看到Class所在的dex和caller 方法所在的dex不一致时,就直接返回空了。
  3. 所以使用方法签名来找,这个应该可以找到,除非这个Class里面没有定义此方法。
  4. 将找到的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访问

  1. const变量, dex2oat会在代码优化中写死它的值,而不会去class中找一遍。
  2. 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的访问流程已经分析完毕。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值