ART虚拟机系列,在前几篇我们分析了ART虚拟机的创建、JavaVmExt和JNIEnvExt。这篇我们继续展开,我们来分析下ART虚拟机中ClassLinker的作用。首先我们看下ClassLinker初始化的地方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { .......... if (UNLIKELY(IsAotCompiler())) { class_linker_ = new AotClassLinker(intern_table_); } else { // 1. new一个ClassLinker对象 class_linker_ = new ClassLinker( intern_table_, runtime_options.GetOrDefault(Opt::FastClassNotFoundException)); } if (GetHeap()->HasBootImageSpace()) { // 2. InitFromBootImage使用boot镜像的内容初始化这个ClassLinker对象 bool result = class_linker_->InitFromBootImage(&error_msg); if (!result) { ............... } |
ClassLinker的整个初始化过程都是在Runtime::Init
中进行的,我们分两部分进行
1. ClassLinker构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
ClassLinker::ClassLinker(InternTable* intern_table, bool fast_class_not_found_exceptions) : boot_class_table_(new ClassTable()), failed_dex_cache_class_lookups_(0), class_roots_(nullptr), find_array_class_cache_next_victim_(0), init_done_(false), log_new_roots_(false), intern_table_(intern_table), fast_class_not_found_exceptions_(fast_class_not_found_exceptions), jni_dlsym_lookup_trampoline_(nullptr), jni_dlsym_lookup_critical_trampoline_(nullptr), quick_resolution_trampoline_(nullptr), quick_imt_conflict_trampoline_(nullptr), quick_generic_jni_trampoline_(nullptr), quick_to_interpreter_bridge_trampoline_(nullptr), image_pointer_size_(kRuntimePointerSize), visibly_initialized_callback_lock_("visibly initialized callback lock"), visibly_initialized_callback_(nullptr), cha_(Runtime::Current()->IsAotCompiler() ? nullptr : new ClassHierarchyAnalysis()) { CHECK(intern_table_ != nullptr); static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_), "Array cache size wrong."); std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr)); } |
- ClassTable用于记录ClaaLoader中已经加载的类
- 上述代码中,
kFindArrayCacheSize
大小为16, find_array_class_cache_
是GcRoot<mirror::Class>
类型的对象,大小也为kFindArrayCacheSize
- 上述代码中,带有很多trampoline一词的成员变量,这些成员变量是一些函数指针,这些函数指针是用于做函数跳转的。因为ART虚拟机即支持从运行dex中的代码,也支持运行oat中的二进制代码。从dex跳转到oat或者从oat跳转到dex之类的控制就和这些trampoline函数有关
class_roots_
成员变量的类型是GcRoot<mirror::ObjectArray<mirror::Class>>
,借助GcRoot的封装,它实际保存的信息是一个ObjectArray对象,数组中的元素类型则是mirror::Class
类型
2. InitFromBootImage方法分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
bool ClassLinker::InitFromBootImage(std::string* error_msg) { Runtime* const runtime = Runtime::Current(); Thread* const self = Thread::Current(); gc::Heap* const heap = runtime->GetHeap(); // 2.1 通过GetBootImageSpaces获取ImageSpace数组,一个ImageSpace对应的是一个art文件 std::vector<gc::space::ImageSpace*> spaces = heap->GetBootImageSpaces(); CHECK(!spaces.empty()); // 获取art文件头 const ImageHeader& image_header = spaces[0]->GetImageHeader(); uint32_t pointer_size_unchecked = image_header.GetPointerSizeUnchecked(); ......... // 从art文件的ImageHeader中获取部分运行时设置 runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod)); runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod)); runtime->SetImtUnimplementedMethod( image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod)); ....... // GetOatFileManager获取到的是OatFileManager对象 std::vector<const OatFile*> oat_files = runtime->GetOatFileManager().RegisterImageOatFiles(spaces); DCHECK(!oat_files.empty()); // 获取第一个oat文件的文件头,oat文件的文件头在代码中用OatHeader来表示 const OatHeader& default_oat_header = oat_files[0]->GetOatHeader(); // 从OatHeader中获取Trampoline方法地址,并设置到对应的变量中去 jni_dlsym_lookup_trampoline_ = default_oat_header.GetJniDlsymLookupTrampoline(); jni_dlsym_lookup_critical_trampoline_ = default_oat_header.GetJniDlsymLookupCriticalTrampoline(); quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline(); ......... // 2.1 GetImageRoot返回类型是mirror::Object*,再通过DownCast将mirror::Object转换为ObjectArray<Class>*类型,最后构造GcRoot class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>( ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast( image_header.GetImageRoot(ImageHeader::kClassRoots))); DCHECK_EQ(GetClassRoot<mirror::Class>(this)->GetClassFlags(), mirror::kClassFlagClass); ....... for (size_t i = 0u, size = spaces.size(); i != size; ++i) { std::vector<std::unique_ptr<const DexFile>> dex_files; // 2.2 上述for循环,针对所有boot镜像文件,调用AddImageSpace添加dex文件到dex_files数组 if (!AddImageSpace(spaces[i], ScopedNullHandle<mirror::ClassLoader>(), /*out*/&dex_files, error_msg)) { return false; } // Append opened dex files at the end. boot_dex_files_.insert(boot_dex_files_.end(), std::make_move_iterator(dex_files.begin()), std::make_move_iterator(dex_files.end())); } for (const std::unique_ptr<const DexFile>& dex_file : boot_dex_files_) { OatDexFile::MadviseDexFile(*dex_file, MadviseState::kMadviseStateAtLoad); } // 最后调用FinishInit完成对ClassLinker的初始化 FinishInit(self); VLOG(startup) << __FUNCTION__ << " exiting"; return true; |
2.1 GetImageRoot方法分析下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// 部分解释参考邓凡平老师的《深入理解ART虚拟机》一书 template <ReadBarrierOption kReadBarrierOption> inline ObjPtr<mirror::Object> ImageHeader::GetImageRoot(ImageRoot image_root) const { // GetImageRoots看下方函数解释 ObjPtr<mirror::ObjectArray<mirror::Object>> image_roots = GetImageRoots<kReadBarrierOption>(); // ImageRoot是枚举变量,包含两个有用的值,一个是kDexCaches,值为0,另一个是kClassRoots,值为1 // 下面的Get函数为ObjectArray的成员函数,用于获取指定索引的元素 return image_roots->Get<kVerifyNone, kReadBarrierOption>(static_cast<int32_t>(image_root)); } // GetImageRoots是ImageHeader定义的成员函数,它将返回ImageHeader中的image_roots成员的值, // 其类型是uint32_t,代表某个信息在art文件中的位置。这个uint32_t的值将变成一个ObnectArray<Object>数组对象 // GetImageRoots内部通过reinterpret_cast进行数据类型转换,从而得到下面的image_roots变量 template <ReadBarrierOption kReadBarrierOption> inline ObjPtr<mirror::ObjectArray<mirror::Object>> ImageHeader::GetImageRoots() const { mirror::ObjectArray<mirror::Object>* image_roots = reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(image_roots_); mirror::ObjectArray<mirror::Object>* result = ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Object>, kReadBarrierOption>( &image_roots); DCHECK_EQ(image_roots, result); return image_roots; } |
对上面的内容进行一个总结:
ImageHeader
的GetImageRoot
返回的是mirror::Object*
指针,但它实际上是一个Object Array<mirror::Class>
对象。所以,InitFromBootImage
第一部分的最后通过down_cast宏将其向下转换成了子类类型的对象。最后再构造一个GcRoot对象赋值给class_roots_。- 而这个数组的内容又是来自于art文件的image_roots_所在的地方。也就说,class_roots_的内容保存在art文件的image_roots_所在的区域。
如下图(来自《深入理解ART虚拟机》一书)进一步展示了上述内容。
- 左边是一个art文件,它会被映射到虚拟机进程,相关信息都是从这块内存中读取的。
ImageHeader
是art文件的头部信息。image_roots_是ImageHeader
中的成员变量。在代码中,它的数据类型是uint32_t,其含义有两种解读方法:对映射到内存里的art文件来说,image_roots_指向某个内存地址;对文件来说,它指向art文件中的某个位置。 - 我们更需要关注这块位置中存储的信息是什么。由上面代码内容可知,image_roots_所指向的那块区域存储的是一个
ObjectArray<Object>
数组。这个数组里有两个元素,其索引位置由ImageRoot枚举来描述,其中kDexCaches取值为0,kImageRoots取值为1。 - 继续来看这个
ObjectArray<Object>
。虽然其模板参数的类型是Object。但对kImageRoots而言,这个Object其实又是一个ObjectArray<Class>
数组。即kImageRoots元素本身又是一个数组,这个数组里元素的类型是Class。
所以,简单来说,ImageHeader的image_roots_所指向的那块区域包含两组信息:
- 第一个是
ObjectArray<DexCache>
数组。 - 第二个是
ObjectArray<Class>
数组。
2.2 AddImageSpace方法分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
// 传入的参数为: // class_loader为一个ScopeNullHander对象。显然,它等于nullptr bool ClassLinker::AddImageSpace( gc::space::ImageSpace* space, Handle<mirror::ClassLoader> class_loader, std::vector<std::unique_ptr<const DexFile>>* out_dex_files, std::string* error_msg) { DCHECK(out_dex_files != nullptr); DCHECK(error_msg != nullptr); const uint64_t start_time = NanoTime(); // 此处app_image为false了 const bool app_image = class_loader != nullptr; const ImageHeader& header = space->GetImageHeader(); // 参考前面关于ART文件的图片,ImageHeader::kDexCaches保存的是dex_caches_object数组 ObjPtr<mirror::Object> dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches); .......... StackHandleScope<3> hs(self); // 下面将获取dex_caches和class_roots数组,他们的类型都是mirror::ObjectArray数组 Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches( hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>())); Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle( header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>())); ............ // 轮询dex_caches for (auto dex_cache : dex_caches.Iterate<mirror::DexCache>()) { std::string dex_file_location = dex_cache->GetLocation()->ToModifiedUtf8(); // 通过OpenOatDexFile打开oat文件中的dex std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file, dex_file_location.c_str(), error_msg); if (dex_file == nullptr) { return false; } if (app_image) { ................ } else { .............. // 虚拟机保存boot class path相关信息,包括 // a. dex文件信息保存到ClassLinker对象的boot_class_patch_成员中 // 其数据类型为vector<const DexFile*> // b. 根据DexFile信息构造DexCache对象,然后将其添加到dex_cahces_成员中, // 其数据类型为list<DexCacheData>。DexCacheData为DexCache的辅助包装, // 定义与ClassLinker内部。DexCache在Java中也有对应类,用于存储从某个 // dex文件中提出出来的各种信息 AppendToBootClassPath(dex_file.get(), dex_cache); } // 将dex_file保存到out_dex_files中 out_dex_files->push_back(std::move(dex_file)); } ............... ClassTable* class_table = nullptr; { WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); // 每一个ClassLoader对象对应有一个class_table_成员。下面将判断ClassLoader的这个ClassTable对象是否存在, // 如果不存在,则创建一个ClassTable对象并将其与ClassLoader对象关联。 // 不过,如果下面这个函数的参数为空(对本例而言,class_loader.Get就是返回nullptr), // 则返回的class_table就是ClassLinker的boot_class_table_成员,它用于保存boot class相关的Class对象。 // 不过此次还没有往这个table中添加数据。 class_table = InsertClassTableForClassLoader(class_loader.Get()); } ClassTable::ClassSet temp_set; // 从art文件中的kSectionClassTable区域提取信息 const ImageSection& class_table_section = header.GetClassTableSection(); const bool added_class_table = class_table_section.Size() > 0u; if (added_class_table) { const uint64_t start_time2 = NanoTime(); size_t read_count = 0; temp_set = ClassTable::ClassSet(space->Begin() + class_table_section.Offset(), /*make copy*/false, &read_count); VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2); } ............. if (added_class_table) { WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); // 最终,来自art文件中kSectionClassTable中的Class信息保存到class_table中 class_table->AddClassSet(std::move(temp_set)); } ........... } |
关于ClassLinker的内容就到这里了,ClassLinker的内容着实博主也是刚刚开始了解,权当自己的学习记录了,可能就没有输出太多的个人见解了。