Android ART虚拟机执行引擎-本地代码的执行(十)
以前面分析的虚拟机的启动流程 ART 虚拟机的启动 为例。
zygote在调用AndroidRuntime的start函数时传入一个class名称:
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
这个classname就是要被执行的类对象,一旦虚拟机启动完成,就会调用这个类的main方法。因为zygote程序本身有一部分本地代码,也有很多java代码的实现,所以它也是需要启动一个虚拟机的。
JniInvocation中的init和startVM函数分别来初始化和启动虚拟机,然后通过onVMCreated()通知拥有者虚拟机的最新状态。接下来就是AndroidRuntime::start()的处理过程。
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
......//执行目标对象的主函数,也即是zygoteInit的main函数 char* slashClassName = toSlashClassName(className); jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); /* keep going */ } else { jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V"); if (startMeth == NULL) { ALOGE("JavaVM unable to find main() in '%s'\n", className); /* keep going */ } else { env->CallStaticVoidMethod(startClass, startMeth, strArray); } }
......
}
先调用toSlashClassName把类名中的“.”转换成“/”,方便虚拟机根据类名查找类的存储地址。
接着主要完成三件事情:
1)找到包含目标class的package包;
2)加载并实例化classname表示的类对象;
3)java代码如何跟OAT中的native code准确对应起来的。
AndroidRuntime是运行在JNI环境的,env变量属于JNIEnv类型,提供了JNI环境下的各种处理函数,查找classname的关键调用是jclass startClass = env->FindClass(slashClassName);经过层层封装,FindClass最终调用了jni_internal.cc中的FindClass。
art/runtime/jni_internal.cc
static jclass FindClass(JNIEnv* env, const char* name) {Runtime* runtime = Runtime::Current();ClassLinker* class_linker = runtime->GetClassLinker();std::string descriptor(NormalizeJniClassDescriptor(name));ScopedObjectAccess soa(env);mirror::Class* c = nullptr;if (runtime->IsStarted()) {StackHandleScope hs(soa.Self());Handle class_loader(hs.NewHandle(GetClassLoader(soa)));c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);} else {c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());}return soa.AddLocalReference(c);}
runtime是进程中的单例,通过它获取到class linker,Class Linker起到类连接器的作用,任何与目标程序以及虚拟机本身相关联的类,类中的方法等都属于它的管理范围。所以findClass的搜索范围包括class_linder中的class类、Jar包、Dex、Image文件等。
传给class_linkder->FindClass的参数:
Thread* self JNIEnv对应的线程;
char* descriptor 类描述符;
Handle
art/runtime/Class_linker.cc
mirror::Class* ClassLinker::FindClass(Thread* self,const char* descriptor,Handle class_loader) {...// Find the class in the loaded classes table.mirror::Class* klass = LookupClass(self, descriptor, hash, class_loader.Get());if (klass != nullptr) { //成功找到class对象return EnsureResolved(self, descriptor, klass);}// Class is not yet loaded.if (descriptor[0] == '[') {//class还没加载,并且是数组类return CreateArrayClass(self, descriptor, hash, class_loader);} else if (class_loader.Get() == nullptr) {//class没加载,但是非数组类// The boot class loader, search the boot class path.ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);//从boot class中搜索if (pair.second != nullptr) {return DefineClass(self,descriptor,hash,ScopedNullHandle(),*pair.first,*pair.second);} else {// The boot class loader is searched ahead of the application class loader, failures are// expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to// trigger the chaining with a proper stack trace.mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();self->SetException(pre_allocated);return nullptr;}} else {//class loader不为null的情况ScopedObjectAccessUnchecked soa(self);mirror::Class* cp_klass;if (FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) {// The chain was understood. So the value in cp_klass is either the class we were looking// for, or not found.if (cp_klass != nullptr) {return cp_klass;}// TODO: We handle the boot classpath loader in FindClassInPathClassLoader. Try to unify this// and the branch above. TODO: throw the right exception here.// We'll let the Java-side rediscover all this and throw the exception with the right stack// trace.}ScopedLocalRef class_loader_object(soa.Env(),soa.AddLocalReference(class_loader.Get()));std::string class_name_string(DescriptorToDot(descriptor));ScopedLocalRef result(soa.Env(), nullptr);{ScopedThreadStateChange tsc(self, kNative);ScopedLocalRef class_name_object(soa.Env(),soa.Env()->NewStringUTF(class_name_string.c_str()));if (class_name_object.get() == nullptr) {DCHECK(self->IsExceptionPending()); // OOME.return nullptr;}CHECK(class_loader_object.get() != nullptr);result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),WellKnownClasses::java_lang_ClassLoader_loadClass,class_name_object.get()));}
......
}
FindClass的目的是找到descriptor所对应的class实现,然后加载到内存,转化为class对象。
首先,通过LookupClass确认这个类是否已经成功加载过;
然后,class _loader为null的情况,根据双亲委派模型,通过调用FindInClassPath在boot class path中查找,
FindClassInPathClassLoader-->
// Search a collection of DexFiles for a descriptor
ClassPathEntry FindInClassPath(const char* descriptor,size_t hash, const std::vector& class_path) {for (const DexFile* dex_file : class_path) {const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash);if (dex_class_def != nullptr) {return ClassPathEntry(dex_file, dex_class_def);}}return ClassPathEntry(nullptr, nullptr);
}
FindInClassPath的实现是在一组DexFile中查找那个文件包含了descriptor描述的类,找到后返回的是一个ClassPathEntry对象,ClassPathEntry是对DexFile文件位置的一种描述方式,找到classpath后,还需要执行加载、实例化、初始化类对象等等操作,才能让这个class真正可用,这以系列的操作是通过ClassLinker::DefineClass来完成的。
在class_loader不为null的情况,尝试调用FindClassInPathClassLoader来查找,这里会查找boot_class_path_下的所有的dexfile,还会查找DexPathList中所有Dex元素;
最后,如果前面都没找到,会通过class_loader.Get()自定义的loadclass来加载目标对象,如果这一步依然失败,那就报 class not found 错误。
在查找到目标对象后,接下来调用DefineClass让class变成可用状态,class的状态有很多种:
art/runtime/mirror/Class.h
enum Status {kStatusRetired = -2, // Retired, should not be used. Use the newly cloned one instead.kStatusError = -1,kStatusNotReady = 0,kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.kStatusLoaded = 2, // DEX idx values resolved.kStatusResolving = 3, // Just cloned from temporary class object.kStatusResolved = 4, // Part of linking.kStatusVerifying = 5, // In the process of being verified.kStatusRetryVerificationAtRuntime = 6, // Compile time verification failed, retry at runtime.kStatusVerifyingAtRuntime = 7, // Retrying verification at runtime.kStatusVerified = 8, // Logically part of linking; done pre-init.kStatusInitializing = 9, // Class init in progress.kStatusInitialized = 10, // Ready to go.kStatusMax = 11,};
具体看下DefineClass的实现:
art/runtime/Class_linker.cc
mirror::Class* ClassLinker::DefineClass(Thread* self,const char* descriptor,size_t hash,Handle class_loader,const DexFile& dex_file,const DexFile::ClassDef& dex_class_def) {StackHandleScope hs(self);auto klass = hs.NewHandle(nullptr);if (klass.Get() == nullptr) {// Allocate a class with the status of not ready.// Interface object should get the right size here. Regular class will// figure out the right size later and be replaced with one of the right// size when the class becomes resolved.
//分配一个class空间,将状态迁移到kStatusNotReadyklass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));}mirror::DexCache* dex_cache = RegisterDexFile(dex_file, class_loader.Get());if (dex_cache == nullptr) {self->AssertPendingOOMException();return nullptr;}klass->SetDexCache(dex_cache);SetupClass(dex_file, dex_class_def, klass, class_loader.Get());// Mark the string class by setting its access flag.if (UNLIKELY(!init_done_)) {if (strcmp(descriptor, "Ljava/lang/String;") == 0) {klass->SetStringClass();}}ObjectLock lock(self, klass);klass->SetClinitThreadId(self->GetTid());// Add the newly loaded class to the loaded classes table.mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash);if (existing != nullptr) {//class_table已经存在这个class// We failed to insert because we raced with another thread. Calling EnsureResolved may cause// this thread to block.return EnsureResolved(self, descriptor, existing);}// Load the fields and other things after we are inserted in the table. This is so that we don't// end up allocating unfree-able linear alloc resources and then lose the race condition. The// other reason is that the field roots are only visited from the class table. So we need to be// inserted before we allocate / fill in these fields.LoadClass(self, dex_file, dex_class_def, klass);if (self->IsExceptionPending()) {VLOG(class_linker) << self->GetException()->Dump();// An exception occured during load, set status to erroneous while holding klass' lock in case// notification is necessary.if (!klass->IsErroneous()) {mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);}return nullptr;}MutableHandle h_new_class = hs.NewHandle(nullptr);if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {// Linking failed.if (!klass->IsErroneous()) {mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);}return nullptr;}return h_new_class.Get();
}
主要操作有AllocClass,SetupClass,InsertClass,LoadClass,LinkClass等。
AllocClass,负责申请Class对象所需的内存空间,这块空间是从Runtime::Current()->GetHeap()中获取的。
SetupClass,是对Class对象进行初始化,InsertClass将本次加载的class保存到一个已经加载的列表中。
LoadClass,是用成员变量、成员函数等来填满AllocClass申请的空间,首先从DexFile中查找Class数据所在的位置,接着通过FindOatClass查找DexClass对应的OatClass对象,然后调用LoadClassMembers加载成员变量、成员函数:
art/runtime/class_linker.cc
void ClassLinker::LoadClassMembers(Thread* self,const DexFile& dex_file,const uint8_t* class_data,Handle klass,const OatFile::OatClass* oat_class) {{// Note: We cannot have thread suspension until the field and method arrays are setup or else// Class::VisitFieldRoots may miss some fields or methods.ScopedAssertNoThreadSuspension nts(self, __FUNCTION__);// Load static fields.// We allow duplicate definitions of the same field in a class_data_item// but ignore the repeated indexes here, b/21868015.LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader());ClassDataItemIterator it(dex_file, class_data);
//加载静态成员变量LengthPrefixedArray* sfields = AllocArtFieldArray(self,allocator,it.NumStaticFields());size_t num_sfields = 0;uint32_t last_field_idx = 0u;for (; it.HasNextStaticField(); it.Next()) {uint32_t field_idx = it.GetMemberIndex();DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier.if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) {DCHECK_LT(num_sfields, it.NumStaticFields());LoadField(it, klass, &sfields->At(num_sfields));++num_sfields;last_field_idx = field_idx;}}// Load instance fields.加载普通成员变量LengthPrefixedArray* ifields = AllocArtFieldArray(self,allocator,it.NumInstanceFields());size_t num_ifields = 0u;last_field_idx = 0u;for (; it.HasNextInstanceField(); it.Next()) {uint32_t field_idx = it.GetMemberIndex();DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier.if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) {DCHECK_LT(num_ifields, it.NumInstanceFields());LoadField(it, klass, &ifields->At(num_ifields));++num_ifields;last_field_idx = field_idx;}}// Set the field arrays.klass->SetSFieldsPtr(sfields);DCHECK_EQ(klass->NumStaticFields(), num_sfields);klass->SetIFieldsPtr(ifields);DCHECK_EQ(klass->NumInstanceFields(), num_ifields);// Load methods.加载成员函数,klass->SetMethodsPtr(AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()),it.NumDirectMethods(),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;// TODO These should really use the iterators.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 casemethod->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++;}}
}
加载的类信息,通过klass变量保存。
DefineClass的最后一步是LinkClass,建立各个class之间的关联,如父类、子类间的重载,多态性的Vtable和Itable等。
其中VTable就是Virtual Method table,用在类的继承关系时,解决多态性的问题。当在class中定义一个虚函数,Compiler会为他添加一个隐藏的成员变量,这个隐藏的成员变量会指向Vtable这个函数指针数组,这个数组的各个指针的具体值需要在运行时得到最终确定。如LinkMethods的实现:
bool ClassLinker::LinkMethods(Thread* self,Handle klass,Handle> interfaces,bool* out_new_conflict,ArtMethod** out_imt) {self->AllowThreadSuspension();// A map from vtable indexes to the method they need to be updated to point to. Used because we// need to have default methods be in the virtuals array of each class but we don't set that up// until LinkInterfaceMethods.std::unordered_map default_translations;// Link virtual methods then interface methods.// We set up the interface lookup table first because we need it to determine if we need to update// any vtable entries with new default method implementations.return SetupInterfaceLookupTable(self, klass, interfaces)&& LinkVirtualMethods(self, klass, /*out*/ &default_translations)&& LinkInterfaceMethods(self, klass, default_translations, out_new_conflict, out_imt);
}
先链接虚函数,在链接接口中的函数。
iftable就是指Interface table,是指多个接口及其对应method列表的集合,他解决的是类的implements关系,而Vtable解决的是类的extends关系。
到这里FindClass的实现就完成了。
下面是如何执行class中的函数。
前面的分析都是从AndroidRuntime::start方法开始的,
frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{/** Start VM. This thread becomes the main thread of the VM, and will* not return until the VM exits.*/char* slashClassName = toSlashClassName(className);jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);/* keep going */} else {env->CallStaticVoidMethod(startClass, startMeth, strArray);}}}
前面的分析都是从jclass startClass = env->FindClass(slashClassName);这句调用开始的。
接下来,先是GetStaticMethodID来从前面加载的class中获取main函数的methodID,同样调用的是jni_internal.cc中的方法。
主要的执行函数是:
art/runtime/jni_internal.cc
static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,const char* name, const char* sig, bool is_static)SHARED_REQUIRES(Locks::mutator_lock_) {mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode(jni_class));if (c == nullptr) {return nullptr;}ArtMethod* method = nullptr;auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();if (is_static) {method = c->FindDirectMethod(name, sig, pointer_size);} else if (c->IsInterface()) {method = c->FindInterfaceMethod(name, sig, pointer_size);} else {method = c->FindVirtualMethod(name, sig, pointer_size);if (method == nullptr) {// No virtual method matching the signature. Search declared// private methods and constructors.method = c->FindDeclaredDirectMethod(name, sig, pointer_size);}}if (method == nullptr || method->IsStatic() != is_static) {ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static");return nullptr;}return soa.EncodeMethod(method);
}
获取到Method ID后,接着是执行这个函数:
art/runtime/jni_internal.cc
static void CallStaticVoidMethod(JNIEnv* env, jclass, jmethodID mid, ...) {va_list ap;va_start(ap, mid);CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);ScopedObjectAccess soa(env);InvokeWithVarArgs(soa, nullptr, mid, ap);va_end(ap);}
然后调用Reflection.cc中的InvokeWithVarArgs:
art/runtime/Reflection.cc
JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,va_list args)SHARED_REQUIRES(Locks::mutator_lock_) {ArtMethod* method = soa.DecodeMethod(mid);bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();if (is_string_init) {// Replace calls to String. with equivalent StringFactory call.method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));}mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode(obj);uint32_t shorty_len = 0;const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);JValue result;ArgArray arg_array(shorty, shorty_len);arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);InvokeWithArgArray(soa, method, &arg_array, &result, shorty);if (is_string_init) {// For string init, remap original receiver to StringFactory result.UpdateReference(soa.Self(), obj, result.GetL());}return result;
} 先通过DecodeMethod把methodID转成ArtMethod,如果是静态方法可以直接调用,否则还要指定它对应的对象receiver。
然后通过InvokeWithArgArray,进一步调用ArtMethod::Invoke:
art/runtime/Art_method.cc
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,const char* shorty) {...// Push a transition back into managed code onto the linked list in thread.ManagedStack fragment;self->PushManagedStackFragment(&fragment);Runtime* runtime = Runtime::Current();// Call the invoke stub, passing everything as arguments.// If the runtime is not yet started or it is required by the debugger, then perform the// Invocation by the interpreter, explicitly forcing interpretation over JIT to prevent// cycling around the various JIT/Interpreter methods that handle method invocation.
//runtime未启动,或者处于debug状态,或者要求使用interpreter执行,if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this))) {if (IsStatic()) {art::interpreter::EnterInterpreterFromInvoke(self, this, nullptr, args, result, /*stay_in_interpreter*/ true);} else {mirror::Object* receiver =reinterpret_cast*>(&args[0])->AsMirrorPtr();art::interpreter::EnterInterpreterFromInvoke(self, this, receiver, args + 1, result, /*stay_in_interpreter*/ true);}} else {//非解释器执行的情况,直接执行函数对应的native codeDCHECK_EQ(runtime->GetClassLinker()->GetImagePointerSize(), sizeof(void*));constexpr bool kLogInvocationStartAndReturn = false;bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;if (!IsStatic()) {(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);} else {(*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);}}} else {}}// Pop transition.self->PopManagedStackFragment(fragment);
}
invoke通过art_quick_invoke_stub,art_quick_invoke_static_stub来执行目标函数。这两个函数最终都是调用quick_invoke_reg_setup来完成执行过程。
art/runtime/arch/arm/quick_entrypoints_cc_arm.cc
static void quick_invoke_reg_setup(ArtMethod* method, uint32_t* args, uint32_t args_size,Thread* self, JValue* result, const char* shorty) {}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
