graph TB
EnsureInitialized-->InitializeClass
InitializeClass-->MethodVerifier::VerifyClass
MethodVerifier::VerifyClass-->MethodVerifier::VerifyMethods
MethodVerifier::VerifyMethods-->MethodVerifier::VerifyMethod
MethodVerifier::VerifyMethod-->MethodVerifier::CodeFlowVerifyInstruction
MethodVerifier::CodeFlowVerifyInstruction-->ResolveType
MethodVerifier::VerifyMethod-->ResolveType
ResolveType-->FindClass
FindClass-->DefineClass
ClassLinker中其他一些常见函数。包括:
·Resolve相关函数。虽然名称叫Resolve,但在ART代码里,它并非是图8-5类加载、链接和初始化阶段中提到的Resolve。相反,它甚至可能触发一个类的加载和链接流程。
·FindClass:根据类的字符串名称搜索一个类。如果没有的话,则可能触发类的加载和链接流程。
graph LR
ResolveMethod-->ResolveType
ResolveField-->ResolveType
ResolveType-->FindClass
FindClass-->FindClassInBaseDexClassLoader-->FindClassInBaseDexClassLoaderClassPath-->DefineClass
template <ClassLinker::ResolveMode kResolveMode>
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) {
//和ResolveType类似,首先判断dex_cache中是否已经解析过这个方法了。
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx,
image_pointer_size_);
if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
if (kResolveMode == ClassLinker::kForceICCECheck) {
//Java有诸如1.5、1.6这样的版本,在早期Java版本里,有些信息和现在的版本有差异,
//此处将检查是否有信息不兼容的地方(即check incompatible class change),
//如果检查失败,则会设置一个IncompatibleClassChangeError异常,笔者此处不拟讨论。
if (resolved->CheckIncompatibleClassChange(type)) {
...... //设置异常
return nullptr;
}
}
return resolved;
}
}
// 如果dex_cache里并未缓存,则先解析该方法所在类的类型(由method_id.class_idx_表示)。
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_,
dex_cache, class_loader);//main
......
switch (type) { //type是指该函数的调用类型
case kDirect:
case kStatic:
/*FindDirectMethod是mirror Class的成员函数,有三个同名函数。在此处调用的函数中,
将沿着klass向上(即搜索klass的父类、祖父类等)搜索类所声明的direct方法,然后比较
这些方法的method_idx是否和输入的method_idx一样。如果一样,则认为找到目标函数。注
意,使用这种方法的时候需要注意比较method_idx是否相等时只有在二者保存在同一个DexCache
对象时才有意义。显然,这种一种优化搜索方法。 */
resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx,
image_pointer_size_);
break;
case kInterface:
if (UNLIKELY(!klass->IsInterface())) { return nullptr; }
else { //如果调用方式是kInterface,则搜索klass及祖父类中的virtual方法以及所
//实现的接口类里的成员方法。
resolved = klass->FindInterfaceMethod(dex_cache.Get(),
method_idx, image_pointer_size_);
}
break;
...... //其他处理
break;
default: UNREACHABLE();
}
//如果通过method_idx未找到对应的ArtMethod对象,则尝试通过函数名及签名信息再次搜索。
//通过签名信息来查找匹配函数的话就不会受制于同一个DexCache对象的要求,但比较字符串的
//速度会慢于上面所采用的比较整型变量method_idx的处理方式。
if (resolved == nullptr) {
//name是指函数名
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
//signature包含了函数的签名信息,就是函数参数及返回值的类型信息
const Signature signature = dex_file.GetMethodSignature(method_id);
switch (type) {
case kDirect:
case kStatic: //调用另外一个FindDirectMethod,主要参数是signature
resolved = klass->FindDirectMethod(name, signature,
image_pointer_size_);
break;
......
}
}
if (LIKELY(resolved != nullptr
&& !resolved->CheckIncompatibleClassChange(type))) {
//如果找到这个方法,则将其存到dex_cache对象中,以method_idx为索引,存储在它的resolved_methods_成员中
dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);
return resolved;
} else { ....../*其他处理 */ }
}
ArtField* ClassLinker::ResolveField(const DexFile& dex_file,
uint32_t field_idx,Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,bool is_static) {
//如果已经解析过该成员变量,则返回
ArtField* resolved = dex_cache->GetResolvedField(field_idx,
image_pointer_size_);
if (resolved != nullptr) { return resolved; }
const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
Thread* const self = Thread::Current();
StackHandleScope<1> hs(self);
//先找到该成员变量对应的Class对象
Handle<mirror::Class> klass(
hs.NewHandle(ResolveType(dex_file, field_id.class_idx_,
dex_cache, class_loader)));
.....
//下面这段代码用于从Class对象ifields_或sfields_中找到对应成员变量的ArtField对象。注
//意,在搜索过程中,会向上遍历Class派生关系树上的基类。
if (is_static) {
resolved = mirror::Class::FindStaticField(self, klass,dex_cache.Get(), field_idx);
} else {
resolved = klass->FindInstanceField(dex_cache.Get(), field_idx);
}
......
//保存到DexCache resolved_fields_成员变量中
dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_);
return resolved;
}
mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
uint16_t type_idx,......) {
/*dex_cache的类型为mirror::DexCache,这里直接回顾本章上文对DexCache类的介绍。
它包含如下几个关键成员变量:
(1)dex_file_(类型为uint64_t):实际为DexFile*,指向该对象关联的那个Dex文件。
(2)resolved_fields_(uint64_t):实际为ArtField*,指向ArtField数组,成员的数据类
型为ArtField。该数组存储了一个Dex文件中定义的所有类的成员变量。另外,只有那些经解
析后得到的ArtField对象才会存到这个数组里。该字段和Dex文件里的field_ids数组有关。
(3)resolved_methods_(uint64_t):实际为ArtMethod*,指向ArtMethod数组,成员的
数据类型为ArtMethod。该数组存储了一个Dex文件中定义的所有类的成员函数。另外,只有
那些经解析后得到的ArtMethod对象才会存到这个数组里。该字段和Dex文件里的
method_ids数组有关。
(4)resolved_string_(uint64_t):实际为GCRoot<String>*,指向GcRoot<String>数
组,包括该dex文件里使用的字符串信息数组。String是mirror::String。该字段和Dex
文件的string_ids数组有关
(5)resolved_classes_(uint64_t):实际为GCRoot<Class>*,指向GcRoot<Class>数组,成
员的数据类型为GcRoot<Class>,存储该dex文件里使用的数据类型信息数组。该字段和Dex
文件里的type_ids数组有关 */
//从dex_cache里找到是否已经缓存过type_idx所代表的那个Class信息
mirror::Class* resolved = dex_cache->GetResolvedType(type_idx);
if (resolved == nullptr) { //如果没有缓存过,则需要找到并存起来
Thread* self = Thread::Current();
//找到这个type的字符串描述
const char* descriptor = dex_file.StringByTypeIdx(type_idx);
//搜索这个字符串对应的类是否存在。FindClass在第7章中曾简单介绍过,后文还会详细讨论它
resolved = FindClass(self, descriptor, class_loader);//main
if (resolved != nullptr) { //类信息保存到DexCache对象中
dex_cache->SetResolvedType(type_idx, resolved);
} else {
...... //抛NoClassDefFoundError异常
ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor);
}
return resolved;
}
mirror::Class* ClassLinker::FindClass(Thread* self,
const char* descriptor,
Handle<mirror::ClassLoader> class_loader) {
//如果字符串只有一个字符,则将搜索基础数据类对应的Class对象
if (descriptor[1] == '\0') {
// only the descriptors of primitive types should be 1 character long, also avoid class lookup
// for primitive classes that aren't backed by dex files.
return FindPrimitiveClass(descriptor[0]);
}
//搜索引用类型对应的类对象,首先根据字符串名计算hash值
const size_t hash = ComputeModifiedUtf8Hash(descriptor);
// Find the class in the loaded classes table.
//从ClassLoader对应的ClassTable中根据hash值搜索目标类
ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, class_loader.Get());
//如果目标类已经存在,则确保它的状态大于或等于kStatusResoved。
//EnsureResolved并不会调用上文提到的实际加载或链接类的函数,它只是等待其他线程完成这个工作
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
if (descriptor[0] != '[' && class_loader == nullptr) {
// Non-array class and the boot class loader, search the boot class path.
/*对bootstrap类而言,它们是由虚拟机加载的,所以没有对应的ClassLoader。
下面的FindInClassPath函数返回的ClassPathEntry是类型别名,其定义如下:
typedef pair<const DexFile*,const DexFile::ClassDef*> ClassPathEntry
*/
//FindInClassPath将从boot class path里对应的文件中找到目标类所在的Dex文件和对应
//的ClassDef信息,然后调用DefineClass来加载目标类
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self,
descriptor,
hash,
ScopedNullHandle<mirror::ClassLoader>(),
*pair.first,
*pair.second);
}
}
ObjPtr<mirror::Class> result_ptr;
bool descriptor_equals;
//如果搜索的是数组类,则创建对应的数组类类对象。
if (descriptor[0] == '[') {
result_ptr = CreateArrayClass(self, descriptor, hash, class_loader);
} else {
//需要触发ClassLoader进行类加载(会调用defineClass启动类加载)。该函数请读者在学完8.7.9节
ScopedObjectAccessUnchecked soa(self);
//7.0上对应的方法是FindClassInPathClassLoader
bool known_hierarchy =
FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr);
}
if(result_ptr == nullptr) {
/*如果通过ClassLoader加载目标类失败,则下面的代码将转入Java层去执行ClassLoader的类
加载。根据代码中的注释所言,类加载失败需要抛出异常,而上面的FindClassInPathClass-
Loader并不会添加异常信息,相反,它还会去掉其执行过程中其他函数因处理失败(比如Define-
Class)而添加的异常信息。所以,接下来的代码将进入Java层去ClassLoader对象的load-
Class函数,虽然目标类最终也会加载失败,但相关异常信息就能添加,同时整个调用的堆栈信息
也能正确反映出来。所以,这种处理方式应是ART虚拟机里为提升运行速度所做的优化处理吧 */
result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
WellKnownClasses::java_lang_ClassLoader_loadClass,
class_name_object.get()));
}
......
// success, return mirror::Class*
return result_ptr.Ptr();
}
mirror::Class* ClassLinker::LookupClass(Thread* self,
const char* descriptor,
size_t hash,
ObjPtr<mirror::ClassLoader> class_loader) {
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
ClassTable* const class_table = ClassTableForClassLoader(class_loader);
if (class_table != nullptr) {
ObjPtr<mirror::Class> result = class_table->Lookup(descriptor, hash);
if (result != nullptr) {
return result.Ptr();
}
}
return nullptr;
}
ClassTable* ClassLinker::ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) {
return class_loader == nullptr ? boot_class_table_.get() : class_loader->GetClassTable();
}
art/runtime/class_table.cc
mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) {
DescriptorHashPair pair(descriptor, hash);
ReaderMutexLock mu(Thread::Current(), lock_);
for (ClassSet& class_set : classes_) {
auto it = class_set.FindWithHash(pair, hash);
if (it != class_set.end()) {
return it->Read();
}
}
return nullptr;
}
bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
ObjPtr<mirror::Class>* result) {
// Termination case: boot class loader.
if (IsBootClassLoader(soa, class_loader.Get())) {
*result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
return true;
}
if (IsPathOrDexClassLoader(soa, class_loader)) {
// For regular path or dex class loader the search order is:
// - parent
// - class loader dex files
// Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result)) {
return false; // One of the parents is not supported.
}
if (*result != nullptr) {
return true; // Found the class up the chain.
}
// Search the current class loader classpath.
*result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
return true;
}
ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath(
ScopedObjectAccessAlreadyRunnable& soa,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader) {
ObjPtr<mirror::Class> ret;
auto define_class = [&](const DexFile* cp_dex_file) REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile::ClassDef* dex_class_def =
OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
if (dex_class_def != nullptr) {
//main
ObjPtr<mirror::Class> klass = DefineClass(soa.Self(),
descriptor,
hash,
class_loader,
*cp_dex_file,
*dex_class_def);
if (klass == nullptr) {
CHECK(soa.Self()->IsExceptionPending()) << descriptor;
soa.Self()->ClearException();
// TODO: Is it really right to break here, and not check the other dex files?
}
ret = klass;
return false; // Found a Class (or error == nullptr), stop visit.
}
return true; // Continue with the next DexFile.
};
VisitClassLoaderDexFiles(soa, class_loader, define_class);
return ret;
}
类的加载、链接和初始化
ClassLinker::DefineClass内部会调用SetupClass和LoadClass
在ART虚拟机实现中,类的加载、链接及初始化过程可以很容易地从代表类状态Status枚举变量的定义里推导出来的。整个过程可分为
Load(对应终态为kStatusLoaded)
Resolve(对应终态为kStatusResolved)
Verify(对应终态为kStatusVerified)
Initialized(对应终态为kStatusInitialized)四个步骤。
art/runtime/class_linker.cc
mirror::Class* ClassLinker::DefineClass(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
mirror::DexCache* dex_cache = RegisterDexFile(dex_file, class_loader.Get());
klass->SetDexCache(dex_cache);
SetupClass(dex_file, dex_class_def, klass, class_loader.Get());//main
// Add the newly loaded class to the loaded classes table.
mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash);
// 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);//load, main
LoadSuperAndInterfaces(klass, dex_file)//main
LinkClass(self, descriptor, klass, interfaces, &h_new_class) //reslove,main
......
return h_new_class.Get();
sequenceDiagram
ClassLinker->>ClassLinker: SetupClass
activate ClassLinker
Note right of ClassLinker: 根据ClassDef初始化mirror::class对象
deactivate ClassLinker
ClassLinker->>ClassLinker: LoadClassMembers
activate ClassLinker
ClassLinker->>ClassLinker: AllocArtFieldArray
activate ClassLinker
Note right of ClassLinker: 依次Alloc和Load静态和非静态field,结果保存在LengthPrefixedArray<ArtField>* ifields,并设置到mirror::class
deactivate ClassLinker
ClassLinker->>ClassLinker: LoadField
activate ClassLinker
deactivate ClassLinker
ClassLinker->>ClassLinker: AllocArtMethodArray
activate ClassLinker
Note right of ClassLinker: 遍历direct和virtual方法,逐个调用loadMethod和LinkCode
deactivate ClassLinker
ClassLinker->>ClassLinker: LoadMethod
activate ClassLinker
deactivate ClassLinker
ClassLinker->>ClassLinker: LinkCode
activate ClassLinker
Note right of ClassLinker: 设置entry_point_from_quick_compiled_code_,oat_class != nullptr时设置为GetQuickCode()
deactivate ClassLinker
ClassLinker->>ClassLinker: LoadSuperAndInterfaces
activate ClassLinker
Note right of ClassLinker: 调用ResolveType触发superClass和interfaces的类加载,更新当前klass状态为kStatusLoaded
deactivate ClassLinker
deactivate ClassLinker
void ClassLinker::SetupClass(const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass, mirror::ClassLoader* class_loader) {
const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
//SetClass是Class的基类mirror Object中的函数。Class也是一种Object,所以此处设置它的
//类类型为”java/lang/Class”对应的那个Class对象
klass->SetClass(GetClassRoot(kJavaLangClass));
uint32_t access_flags = dex_class_def.GetJavaAccessFlags();
//设置访问标志及该类的加载器对象
klass->SetAccessFlags(access_flags);
klass->SetClassLoader(class_loader);
//设置klass的状态为kStatusIdx。
mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, nullptr);
//设置klass的dex_class_def_idx_和dex_type_idx_成员变量。
klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
klass->SetDexTypeIndex(dex_class_def.class_idx_);
}
void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass) {
//class_data的内容就是图8-7中的class_data_item
const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
if (class_data == nullptr) { return; }
bool has_oat_class = false;
if (Runtime::Current()->IsStarted() &&
!Runtime::Current()->IsAotCompiler()) {
//如果不是编译虚拟机的话,则先尝试找到该类经dex2oat编译得到的OatClass信息
OatFile::OatClass oat_class = FindOatClass(dex_file,
klass->GetDexClassDefIndex(), &has_oat_class);
if (has_oat_class) {
LoadClassMembers(self, dex_file, class_data, klass, &oat_class);
}
}
//不管有没有OatClass信息,最终调用的函数都是LoadClassMembers。
if (!has_oat_class) {
LoadClassMembers(self, dex_file, class_data, klass, nullptr);
}
}
void ClassLinker::LoadClassMembers(Thread* self,
const DexFile& dex_file,const uint8_t* class_data,
Handle<mirror::Class> klass,const OatFile::OatClass* oat_class) {
//注意这个函数的参数,class_data为dex文件里的代表该类的class_data_item信息,
//而oat_class描述的是Oat文件里针对这个类提供的一些信息
{
LinearAlloc* const allocator =
GetAllocatorForClassLoader(klass->GetClassLoader());
//创建class_data_item迭代器
ClassDataItemIterator it(dex_file, class_data);
//分配用于存储目标类静态成员变量的固定长度数组sfields
LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(
self,allocator,it.NumStaticFields());
size_t num_sfields = 0;
uint32_t last_field_idx = 0u;
//遍历class_data_item中的静态成员变量数组,然后填充信息到sfields数组里
for (; it.HasNextStaticField(); it.Next()) {
uint32_t field_idx = it.GetMemberIndex();
if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) {
//加载这个ArtField的内容。下文将单独介绍此函数
LoadField(it, klass, &sfields->At(num_sfields));
++num_sfields;
last_field_idx = field_idx;
}
}
// 同理,分配代表该类非静态成员变量的数组
LengthPrefixedArray<ArtField>* 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();
if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) {
LoadField(it, klass, &ifields->At(num_ifields)); //类似的处理
++num_ifields;
last_field_idx = field_idx;
}
}
//设置Class类的sfields_和ifields_成员变量
klass->SetSFieldsPtr(sfields);
klass->SetIFieldsPtr(ifields);
/*设置Class类的methods_成员变量。读者可回顾笔者对该成员变量的解释,它是一个Length-
PrefixedArray<ArtMethod>数组,其元素布局为
(1)[0,virtual_methods_offset_)为本类包含的direct成员函数
(2)[virtual_methods_offset_,copied_methods_offset_)为本类包含的virtual
成员函数
(3)[copied_methods_offset_,...)为剩下的诸如miranda函数等内容。下面代码中,
先分配1和2所需要的元素空间,然后设置klass对应的成员变量,其中:
klass->methods_为AllocArtMethodArray的返回值,
klass->copied_methods_offset_为类direct和virtual方法个数之和
klass->virtual_methods_offset_为类direct方法个数 */
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;
//遍历direct方法数组,加载它们然后关联字节码
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetDirectMethodUnchecked(i,
image_pointer_size_);
//加载ArtMethod对象,并将其和字节码关联起来。
LoadMethod(self, dex_file, it, klass, method);
//注意,oat_class信息只在LinkCode中用到。LinkCode留待10.1节介绍
LinkCode(method, oat_class, class_def_method_index);
uint32_t it_method_index = it.GetMemberIndex();
if (last_dex_method_index == it_method_index) {
method->SetMethodIndex(last_class_def_method_index);
} else { //设置ArtMethod的method_index_,该值其实就是这个ArtMethod
//位于上面klass methods_数组中的位置
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方法。注意,对表示virtual方法的ArtMethod对象而言,它们的method_index_
//和klass methods_数组没有关系,也不在下面的循环中设置。
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
...... //和direct方法处理一样,唯一不同的是,此处不调用ArtMethod的SetMethod-Index函数,即不设置它的method_index_成员
}
}
}
void ClassLinker::LoadField(const ClassDataItemIterator& it,
Handle<mirror::Class> klass, ArtField* dst) {
const uint32_t field_idx = it.GetMemberIndex();
dst->SetDexFieldIndex(field_idx); //设置对应于dex文件里的那个field_idx
dst->SetDeclaringClass(klass.Get()); //设置本成员变量由哪个Class对象定义
dst->SetAccessFlags(it.GetFieldAccessFlags()); //设置访问标记
}
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_);
//设置ArtMethod declaring_class_和dex_method_index_和成员变量
dst->SetDexMethodIndex(dex_method_idx);
dst->SetDeclaringClass(klass.Get());
//设置dex_code_item_offset_成员变量
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
//设置ArtMethod ptr_sized_fields_结构体中的dex_cache_resolved_methods_和dex_cache_
//resolved_types_成员。读者可回顾上文对ArtMethod成员变量的介绍
dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(),
image_pointer_size_);
dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes(),
image_pointer_size_);
uint32_t access_flags = it.GetMethodAccessFlags();
//处理访问标志。比如,如果函数名为”finalize”的话,设置该类为finalizable
if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) {
//该类的class loader如果不为空,则表示不是boot class,也就是系统所必需的那些
//基础类
if (klass->GetClassLoader() != nullptr){
//设置类的访问标记,增加kAccClassIsFinalizable。表示该类重载了finalize函数
klass->SetFinalizable();
} else {...... }
}
} else if (method_name[0] == '<') {
......//如果函数名为”<init>”或”<clinit>”,则设置访问标志位kAccConstructor
}
dst->SetAccessFlags(access_flags);
}
void ClassLinker::LinkCode(ArtMethod* method,
const OatFile::OatClass* oat_class,
int32_t class_def_method_index) {
Runtime* const runtime = Runtime::Current();
if (runtime->IsAotCompiler()) {
// The following code only applies to a non-compiler runtime.
return;
}
// Method shouldn't have already been linked.
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
/*在下面的代码中:
(1)oat_class的类型为OatFile::OatClass,其内容由oat文件中OatClass区域相应位置处的信息构成。
(2)oat_method的类型为OatFile::OatMethod,其内容由oat文件中OatMethod区域对应的OatQuickMethodHeader信息构成。*/
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
if (oat_class != nullptr) {
/*获取该Java方法对应的OatMethod信息。如果它没有被编译过,则返回的OatMethod对象的code_
offset_取值为0。OatMethod.code_offset_指向对应机器码在oat文件中的位置。其值为0
就表示该方法不存在机器码。 */
const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);
/*设置ArtMethod ptr_sized_fields_entry_point_from_quick_compiled_code_为
Oat文件区域OatQuickMethodHeader的code_。读者可回顾第9章图9-41“oat和art文件
的关系”。code_处存储的就是该方法编译得到的机器码。注意,为节省篇幅,笔者以后用机器
码入口地址来指代entry_point_from_quick_compiled_code_成员变量。*/
oat_method.LinkMethod(method);
}
// Install entry point from interpreter.
const void* quick_code = method->GetEntryPointFromQuickCompiledCode();//获取ArtMethod对象的机器码入口地址
/*在ShouldUseInterpreterEntrypoint函数中,如果机器码入口地址为空(该方法没有经过编译),
或者虚拟机进入了调试状态,则必须使用解释执行的模式。这种情况下,该函数返回值enter_inter-
preter为true。 */
bool enter_interpreter = ShouldUseInterpreterEntrypoint(method, quick_code);
if (method->IsStatic() && !method->IsConstructor()) {
// For static methods excluding the class initializer, install the trampoline.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
/*如果method为静态且不是类初始化"<clinit>"(它是类的静态构造方法)方法,则设置机器码入口
地址为art_quick_resolution_trampoline。根据9.5.4.4.1节的介绍可知。该地址对应的是
一段跳转代码,跳转的目标是artQuickResolutionTrampoline函数。它是一个特殊的函数,和
类的解析有关。注意,虽然在LinkCode中(该函数是在类初始化之前被调用的)设置的跳转目标为
artQuickResolutionTrampoline,但ClassLinker在初始化类的InitializeClass函数的最
后会通过调用FixupStaticTrampolines来尝试更新此处所设置的跳转地址为正确的地址, 更新机器码入口地址entry_point_from_quick_compiled_code_*/
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
} else if (quick_code == nullptr && method->IsNative()) {
/*如果method为jni方法,并且不存在机器码,则设置机器码入口地址为跳转代码art_quick_
generic_jni_trampoline,它的跳转目标为artQuickGenericJniTrampoline函数。*/
method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
} else if (enter_interpreter) {
// Set entry point from compiled code if there's no code or in interpreter only mode.
/*enter_interpreter的取值来自ShouldUseInterpreterEntrypoint,一般而言,如果该
方法没有对应的机器码,或者在调试运行模式下,则enter_interpreter为true。对应的机
器码入口地址为art_quick_to_interpreter_bridge跳转代码,其跳转的目标
//为artQuickToInterpreterBridge函数。*/
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
}
if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
/*如果为jni方法,则调用ArtMethod的UnregisterNative函数,其内部主要设置ArtMethod
tls_ptr_sized_.entry_point_from_jni_成员变量为跳转代码art_jni_dlsym_look-
up_stub,跳转目标为artFindNativeMethod函数。为简单起见,笔者以后用jni机器码入口
地址指代entry_point_from_jni_成员变量。这部分内容和JNI有关,我们以后再讨论它。*/
method->UnregisterNative();
if (enter_interpreter || quick_code == nullptr) {
// We have a native method here without code. Then it should have either the generic JNI
// trampoline as entrypoint (non-static), or the resolution trampoline (static).
// TODO: this doesn't handle all the cases where trampolines may be installed.
const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
DCHECK(IsQuickGenericJniStub(entry_point) ||
IsQuickResolutionStub(entry_point));
}
}
}
art/runtime/oat_file.cc
void OatFile::OatMethod::LinkMethod(ArtMethod* method) const {
CHECK(method != nullptr);
method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
}
bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass,
const DexFile& dex_file) {
const DexFile::ClassDef& class_def =
dex_file.GetClassDef(klass->GetDexClassDefIndex());
//找到基类的id
uint16_t super_class_idx = class_def.superclass_idx_;
//根据基类的id来解析它,返回值是代表基类的Class实例。
//ResolveType函数的内容将在8.7.8节中介绍
mirror::Class* super_class = ResolveType(dex_file, super_class_idx,
klass.Get());
if (super_class == nullptr) { return false; }
//做一些简单校验。比如,基类如果不允许派生,则返回失败
if (!klass->CanAccess(super_class)) { return false; }
klass->SetSuperClass(super_class);
//设置super_class_成员变量的值
//下面这个检查和编译有关系,笔者不拟讨论它,代码中的注释非常详细
if (!CheckSuperClassChange(klass, dex_file, class_def, super_class)) {
return false;
}
//从dex文件里找到目标类实现了哪些接口类。参考图8-7所示class_def结构体中
//interfaces_off的含义
const DexFile::TypeList* interfaces =
dex_file.GetInterfacesList(class_def);
if (interfaces != nullptr) {
for (size_t i = 0; i < interfaces->Size(); i++) {
uint16_t idx = interfaces->GetTypeItem(i).type_idx_;
//解析这个接口类。下文将介绍ResolveType函数
mirror::Class* interface = ResolveType(dex_file, idx, klass.Get());
...... //如果接口类找不到或者接口类不允许继承,则返回错误
}
}
//设置klass的状态为kStatusLoaded
mirror::Class::SetStatus(klass, mirror::Class::kStatusLoaded, nullptr);
return true;
}
LinkClass相关函数
进入LinkClass之前,先回顾上文SetupClass和LoadClass的结果。此时:
·目标类的信息从dex文件里对应的class_def结构体及其他相关结构体已经提取并转换为一个mirror Class对象。
·同时,该Class对象中代表本类的成员变量和成员函数信息也相应创建为对应的ArtField和ArtMethod的对象,并做好了相关设置。从Class类成员来说,它的methods_、sfields_、ifields_都设置好了。
bool ClassLinker::LinkClass(Thread* self,const char* descriptor,
Handle<mirror::Class> klass, //待link的目标class
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
MutableHandle<mirror::Class>* h_new_class_out) {
/*注意本函数的参数:
(1)klass代表输入的目标类,其状态是kStatusLoaded。
(2)h_new_class_out为输出参数,代表LinkClass执行成功后所返回给调用者的、类状态切升级为
kStatusResolved的目标类。所以,这个变量才是LinkClass执行成功后的结果。*/
//Link基类
if (!LinkSuperClass(klass)) { return false; }//main
/*下面将创建一个ArtMethod*数组。数组大小为kImtSize。它是一个编译时常量,由编译参数ART_
IMT_SIZE指定,默认是64。IMT是Interface Method Table的缩写。如其名所示,它和接口所
实现的函数有关。其作用我们后文再介绍 */
ArtMethod* imt[mirror::Class::kImtSize];
std::fill_n(imt, arraysize(imt), //填充这个数组的内容为默认值
Runtime::Current()->GetImtUnimplementedMethod());
//对该类所包含的方法(包括它实现的接口方法、继承自父类的方法等)进行处理
if (!LinkMethods(self, klass, interfaces, imt)) { return false;}//main
//下面两个函数分别对类的成员进行处理。
if (!LinkInstanceFields(self, klass)) { return false;}//main
size_t class_size;
//尤其注意LinkStaticFields函数,它的返回值包括class_size,代表该类所需内存大小。
if (!LinkStaticFields(self, klass, &class_size)) { return false; }//main
//处理Class的reference_instance_offsets_成员变量
CreateReferenceInstanceOffsets(klass);//main
//当目标类是基础数据类、抽象类(不包括数组)、接口类时,下面的if条件满足
if (!klass->IsTemp() || .....)) {
.....
//对于非Temp的类,不需要额外的操作,所以klass的状态被置为kStatusResolved,然后再赋
//值给h_new_class_out。到此,目标类就算解析完了
mirror::Class::SetStatus(klass, mirror::Class::kStatusResolved, self);
h_new_class_out->Assign(klass.Get());
} else {//如果目标类是可实例化的,则需要做额外的处理
StackHandleScope<1> hs(self);
//CopyOf很关键,它先创建一个大小为class_size的Class对象,然后,klass的信息将拷贝
//到这个新创建的Class对象中。在这个处理过程汇总,Class对象的类状态将被设置为kStatus-
//Resolving。
auto h_new_class = hs.NewHandle(klass->CopyOf(self, class_size,
imt, image_pointer_size_));
klass->SetMethodsPtrUnchecked(nullptr, 0, 0); //清理klass的内容
...... //清理klass的其他内容;
{
......
//更新ClassTable中对应的信息
mirror::Class* existing = table->UpdateClass(descriptor,
h_new_class.Get(), ComputeModifiedUtf8Hash(descriptor));
......
}
//设置klass的状态为kStatusRetired,表示该类已经被废弃
mirror::Class::SetStatus(klass, mirror::Class::kStatusRetired, self);
//设置新类的状态为kStatusResolved,表示该类解析完毕。
mirror::Class::SetStatus(
h_new_class, mirror::Class::kStatusResolved,self);
h_new_class_out->Assign(h_new_class.Get()); //赋值给输出参数
}
return true;
}
bool ClassLinker::LinkMethods(Thread* self,
Handle<mirror::Class> klass,
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
ArtMethod** out_imt) {
......
std::unordered_map<size_t, ClassLinker::MethodTranslation>
default_translations;
//下面三个函数很复杂
return SetupInterfaceLookupTable(self, klass, interfaces)
&& LinkVirtualMethods(self, klass, &default_translations)
&& LinkInterfaceMethods(self, klass, default_translations, out_imt);
}
bool ClassLinker::LinkInstanceFields(Thread* self, Handle<mirror::Class> klass) {
CHECK(klass.Get() != nullptr);
return LinkFields(self, klass, false, nullptr);
}
bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size) {
CHECK(klass.Get() != nullptr);
return LinkFields(self, klass, true, class_size);
}
bool ClassLinker::LinkFields(Thread* self,
Handle<mirror::Class> klass,
bool is_static, size_t* class_size) {
//确定成员变量的个数
const size_t num_fields = is_static ? klass->NumStaticFields() :
klass->NumInstanceFields();
//从Class中得到代表静态或非静态成员变量的数组
LengthPrefixedArray<ArtField>* const fields = is_static ?
klass->GetSFieldsPtr() : klass->GetIFieldsPtr();
MemberOffset field_offset(0);
if (is_static) {
//如果是静态变量,则得到静态存储空间的起始位置。
field_offset = klass->GetFirstReferenceStaticFieldOffsetDuringLinking(
image_pointer_size_);
} else {
//获取基类的ObjectSize
mirror::Class* super_class = klass->GetSuperClass();
if (super_class != nullptr) {
field_offset = MemberOffset(super_class->GetObjectSize());
}
}
//排序,引用类型放最前面,然后是long/double、int/float等。符合图8-13中的布局要求
std::deque<ArtField*> grouped_and_sorted_fields;
for (size_t i = 0; i < num_fields; i++) {
grouped_and_sorted_fields.push_back(&fields->At(i));
}
std::sort(grouped_and_sorted_fields.begin(),
grouped_and_sorted_fields.end(),LinkFieldsComparator());
size_t current_field = 0;
size_t num_reference_fields = 0;
FieldGaps gaps;
//先处理引用类型的变量
for (; current_field < num_fields; current_field++) {
ArtField* field = grouped_and_sorted_fields.front();
Primitive::Type type = field->GetTypeAsPrimitiveType();
bool isPrimitive = type != Primitive::kPrimNot;
if (isPrimitive) { break; }
......
grouped_and_sorted_fields.pop_front();
num_reference_fields++;
field->SetOffset(field_offset); //设置ArtField的offset_变量
field_offset = MemberOffset(field_offset.Uint32Value() +
sizeof(mirror::HeapReference<mirror::Object>));
}
//我们在ComputeClassSize中曾提到说内存布局可以优化,下面的ShuffleForward就是处理这种优
//化。ShuffleForward是一个模板函数,内部会设置ArtField的offset_。
ShuffleForward<8>(¤t_field, &field_offset,
&grouped_and_sorted_fields, &gaps);
...... //处理4字节、2字节基础数据类型变量
ShuffleForward<1>(¤t_field, &field_offset,
&grouped_and_sorted_fields, &gaps);
......
//特殊处理java.lang.ref.Reference类。将它的非静态引用类型变量的个数减去一个。减去的这个
//变量是java Reference类中的referent成员,它和GC有关,需要特殊对待。后续章节会详细介绍GC。
if (!is_static && klass->DescriptorEquals("Ljava/lang/ref/Reference;")) {
--num_reference_fields;
}
size_t size = field_offset.Uint32Value();
if (is_static) {
klass->SetNumReferenceStaticFields(num_reference_fields);
*class_size = size; //设置Class最终所需内存大小
} else {
klass->SetNumReferenceInstanceFields(num_reference_fields);
......
//如果类的对象是固定大小(像数组、String则属于非固定大小),则设置Object所需内存大小
if (!klass->IsVariableSize()) {
......
klass->SetObjectSize(size);
}
}
return true;
}
在ART虚拟机中,如果某个类校验通过的话,后续执行该类的方法时将跳过所谓的访问检查(Access Check)
graph TB
dex2oat时verify_预校验-->软错误时kStatusRetryVerificationAtRuntime
dex2oat时verify_预校验-->成功kStatusVerified
虚拟机运行时检查-->成功kStatusVerified
成功kStatusVerified-->该类methods_数组所有ArtMethod对象设置kAccSkipAccessChecks标志
成功kStatusVerified-->为类设置kAccVerification-Attempted标记
虚拟机运行时检查-->软错误时kStatusRetryVerificationAtRuntime
软错误时kStatusRetryVerificationAtRuntime-->MethodVerifier对该类进行校验
MethodVerifier对该类进行校验-->依旧软错误
依旧软错误-->为类设置kAccVerification-Attempted标记
void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass,
LogSeverity log_level) {
...... //可能有另外一个线程正在处理类的校验,此处省略的代码将处理这种情况判断该类是否
//已经通过校验(类状态大于或等于kStatusVerified)
if (klass->IsVerified()) {
/* Verify一个类是需要代价的(比如执行上两节代码所示MethodVerifier的相关函数是
需要花费时间),但付出这个代价会带来一定好处。在ART虚拟机中,如果某个类校验通
过的话,后续执行该类的方法时将跳过所谓的访问检查(Access Check)。Access Check
的具体内容将在后续章节介绍。此处举一个简单例子,比如访问检查将判断外部调用者是
否调用了某个类的私有函数。显然,Access Check将影响函数执行的时间。
下面的这个EnsureSkipAccessChecksMethods将做两件事情:
(1)为klass methods_数组里的ArtMethod对象设置kAccSkipAccessChecks标志位
(2)为klass设置kAccVerificationAttempted标志位。这个标记位表示该类已经尝试过
校验了,无须再次校验。
这些标志位的作用我们以后碰见具体代码时再讲解。 */
EnsureSkipAccessChecksMethods(klass);
return;
}
//如果类状态大于等于kStatusRetryVerificationAtRuntime并且当前进程是dex2oat(Is-
//AotCompiler用于判断当前进程是否为编译进程),则直接返回。类的校验将留待真实的虚拟机
//进程来完成。
if (klass->IsCompileTimeVerified() &&
Runtime::Current()->IsAotCompiler()) { return; }
if (klass->GetStatus() == mirror::Class::kStatusResolved) {
//设置类状态为kStatusVerifying,表明klass正处于类校验阶段。
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerifying, self);
} else { //如果类的当前状态不是kStatusResolved,则表明该类在dex2oat时已经做过校
//验,但校验结果是kStatusRetryVerificationAtRuntime。所以此处需要在完
//整虚拟机环境下再做校验。
mirror::Class::SetStatus(klass, mirror::Class::SetStatus(klass,
mirror::Class::kStatusVerifyingAtRuntime, self);
}
/*IsVerificationEnabled用于返回虚拟机是否开启了类校验的功能,它和verify_mode.h中定义
的枚举变量VerifyMode有关。该枚举变量有三种取值:
(1)kNone:不做校验。
(2)kEnable:标准校验流程。其中,在dex2oat过程中会尝试做预校验(preverifying)。
(3)kSoftFail:强制为软校验失败。这种情况下,指令码在解释执行的时候会进行access
check。这部分内容在dex2oat一章中有所体现,以后我们会提到。
从上述内容可知,在ART里,类校验的相关知识绝不仅仅只包含JLS规范中提到的那些诸如检查
字节码是否合法之类的部分,它还和dex2oat编译与Java方法如何执行等内容密切相关。*/
if (!Runtime::Current()->IsVerificationEnabled()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self);
EnsureSkipAccessChecksMethods(klass);
return;
}
//先校验父类。AttemptSuperTypeVerification内部也会调用VerifyClass。
......
//请读者自行阅读它
MutableHandle<mirror::Class> supertype(hs.NewHandle(
klass->GetSuperClass()));
if (supertype.Get() != nullptr && !AttemptSupertypeVerification(self,
klass, supertype)) { return; }
//如果父类校验通过,并且klass不是接口类的话,我们还要对klass所实现的接口类进行校验。校验
//接口类是从Java 1.8开始,接口类支持定义有默认实现的接口函数,默认函数包含实际的内容,所以
//需要校验。
if ((supertype.Get() == nullptr || supertype->IsVerified())
&& !klass->IsInterface()) {
int32_t iftable_count = klass->GetIfTableCount();
MutableHandle<mirror::Class> iface(hs.NewHandle<mirror::Class>(nullptr));
//遍历IfTable,获取其中的接口类。
for (int32_t i = 0; i < iftable_count; i++) {
iface.Assign(klass->GetIfTable()->GetInterface(i));
//接口类没有默认接口函数,或者已经校验通过,则略过
if (LIKELY(!iface->HasDefaultMethods() || iface->IsVerified())) {
continue;
} else if (UNLIKELY(!AttemptSupertypeVerification(self,
klass, iface))) {
return; //接口类校验失败。直接返回
} else if (UNLIKELY(!iface->IsVerified())) {
//如果接口类校验后得到的状态为kStatusVerifyingAtRuntime,则跳出循环
supertype.Assign(iface.Get());
break;
}
}
const DexFile& dex_file = *klass->GetDexCache()->GetDexFile();
mirror::Class::Status oat_file_class_status(
mirror::Class::kStatusNotReady);
/*下面这个函数其实只是用来判断klass是否已经在dex2oat阶段做过预校验了。这需要结合该类编译
结果来决定(包含在OatFile中的类状态)。除此之外,如果我们正在编译系统镜像时(即在dex2oat
进程中编译包含在Boot Image的类),则该函数也返回false。preverified如果为false,则将
调用MethodVerifier VerifyClass来做具体的校验工作。VerifyClassUsingOatFile还包含其
他几种返回false的情况,请读者自行阅读。*/
bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(),
oat_file_class_status);
verifier::MethodVerifier::FailureKind verifier_failure =
verifier::MethodVerifier::kNoFailure;
if (!preverified) { //没有预校验的处理
Runtime* runtime = Runtime::Current();
//调用MethodVerifier VerifyClass来完成具体的校验工作,main
verifier_failure = verifier::MethodVerifier::VerifyClass(self,
klass.Get(), runtime->GetCompilerCallbacks(),,...);
}
......
if (preverified || verifier_failure !=
verifier::MethodVerifier::kHardFailure) {
......
if (verifier_failure == verifier::MethodVerifier::kNoFailure) {
//自己和基类(或者接口类)的校验结果都正常,则类状态设置为kStatusVerified
if (supertype.Get() == nullptr || supertype->IsVerified()) {
mirror::Class::SetStatus(klass,
mirror::Class::kStatusVerified, self);
} else {......}
} else { //对应校验结果为kSoftFail的情况
if (Runtime::Current()->IsAotCompiler()) {
//如果是dex2oat中出现这种情况,则设置类的状态为kStatusRetryVerificationAtRuntime
mirror::Class::SetStatus(klass,
mirror::Class::kStatusRetryVerificationAtRuntime, self);
} else {
/*设置类状态为kStatusVerified,并且设置类标记位kAccVerificationAttempted。
注意,我们在上面代码中介绍过EnsureSkipAccessChecksMethods函数。这个函数将
(1)为klass methods_数组里的ArtMethod对象设置kAccSkipAccessChecks标志位
(2)为klass设置kAccVerificationAttempted标志位。
而下面的代码只设置了(2),没有设置(1)。所以,虽然类状态为kStatusVerified,
但在执行其方法时可能还要做Access Check */
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self);
klass->SetVerificationAttempted();
}
}
} else {...... }
} else {...... }
....... //其他处理,略过
}
MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
mirror::Class* klass,......) { //待校验的类由kclass表示
//如果该类已经被校验过,则直接返回校验成功
if (klass->IsVerified()) { return kNoFailure; }
bool early_failure = false;
std::string failure_message;
//获取该class所在的Dex文件信息及该类在Dex文件里的class_def信息
const DexFile& dex_file = klass->GetDexFile();
const DexFile::ClassDef* class_def = klass->GetClassDef();
//获取该类的基类对象
mirror::Class* super = klass->GetSuperClass();
std::string temp;
//下面这个判断语句表示kclass没有基类,而它又不是Java Object类。显然,这是违背Java语言规范
//的—Java中,只有Object类才没有基类
if (super == nullptr && strcmp("Ljava/lang/Object;",
klass->GetDescriptor(&temp)) != 0) {
early_failure = true;
failure_message = " that has no super class";
} else if (super != nullptr && super->IsFinal()) {
//如果基类有派生类的话,基类不能为final
early_failure = true;
} else if (class_def == nullptr) { //该类在Dex文件里没有class_def信息
early_failure = true;
}
if (early_failure) {
......
if (callbacks != nullptr) {
//callbacks的类型是CompilerCallbacks,dex字节码转机器码的时候会用上它
ClassReference ref(&dex_file, klass->GetDexClassDefIndex());
callbacks->ClassRejected(ref);
}
return kHardFailure; //返回校验错误
}
StackHandleScope<2> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
klass->GetClassLoader()));
//进一步校验
return VerifyClass(self,&dex_file,dex_cache,......);
}
MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
......) {
if ((class_def->access_flags_ & (kAccAbstract | kAccFinal))
== (kAccAbstract | kAccFinal)) {
return kHardFailure; //类不能同时是final又是abstract
}
const uint8_t* class_data = dex_file->GetClassData(*class_def);
if (class_data == nullptr) { return kNoFailure; }
//创建ClassDataItemIterator迭代器对象,通过它可以获取目标类的class_data_item里的内容
ClassDataItemIterator it(*dex_file, class_data);
//不校验类的成员变量
while (it.HasNextStaticField() || it.HasNextInstanceField()) {
it.Next();
}
ClassLinker* linker = Runtime::Current()->GetClassLinker();
//对本类所定义的Java方法进行校验。VerifyMethods在上一节已经介绍过了
MethodVerifier::FailureData data1 = VerifyMethods<true>(self,
linker, dex_file, ......);//main
//校验本类中的virtual_methods数组
MethodVerifier::FailureData data2 = VerifyMethods<false>(self,
linker, dex_file,......);
//将校验结果合并到data1的结果中
data1.Merge(data2);
//校验结果通过合并后的data1的成员来判断
if (data1.kind == kNoFailure) { return kNoFailure; }
else { return data1.kind;
}
}
template <bool kDirect>
MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self,
ClassLinker* linker,const DexFile* dex_file,
const DexFile::ClassDef* class_def,ClassDataItemIterator* it,...) {
/* 注意这个函数的参数和返回值:
(1)该函数为模板函数。结合图8-7,如果模板参数kDirect为true,则校验的将是目标类中
direct_methods的内容,否则为virtual_methods的内容。
(2)class_def的类型为DexFile::ClassDef。它是Dex文件里的一部分,class_def中最重
要的信息存储在class_data_item中,而class_data_item的内容可借助迭代器it来获取。
(3)self代表当前调用的线程对象。dex_file是目标类所在的Dex文件。
(4)返回值的类型为FailureData。它是MethodVeifier定义的内部类,其内部有一个名为kind
的成员变量,类型为枚举FailureKind,取值有如下三种情况:
a) kNoFailure,表示校验无错。
b) kSoftFailure,表示校验软错误,该错误发生在从dex字节码转换为机器码时所做的校验过程
中。编译过程由dex2oat进程完成。dex2oat是一个简单的,仅用于编译的虚拟机进程,它包
含了前文提到的诸如heap、runtime等重要模块,但编译过程不会将所有相关类都加载到虚拟
机里,所以可能会出现编译过程中校验失败的情况。kSoftFailure失败并没有关系,这个类
在后续真正使用之时,虚拟机还会再次进行校验。
c) kHardFailure,表示校验错误,该错误表明校验失败。
*/
MethodVerifier::FailureData failure_data;
int64_t previous_method_idx = -1;
/*同上,如果kDirect为true,则遍历class_data_item信息里的direct_methods数组,
否则遍历其中的virtual_methods数组(代表虚成员函数)。*/
while (HasNextMethod<kDirect>(it)) {
self->AllowThreadSuspension();
uint32_t method_idx = it->GetMemberIndex();
......
previous_method_idx = method_idx;
/*InvokeType是枚举变量,和dex指令码里函数调用的方式有关:取值包括:
(1)kStatic:对应invoke-static相关指令,调用类的静态方法。
(2)kDirect:对应invoke-direct相关指令,指调用那些非静态方法。包括两类,一类是
private修饰的成员函数,另外一类则是指类的构造函数。符合kStatic和kDirect调用类型
函数属于上文所述的direct methods(位于类的direct_methods数组中)。
(3)kVirtual:对应invoke-virtual相关指令,指调用非private、static或final修饰
的成员函数(注意,不包括调用构造函数)。
(4)kSuper:对应invoke-super相关指令,指在子类的函数中通过super来调用直接父类的
函数。注意,dex官方文档只是说invoke-super用于调用直接父类的virtual函数。但笔者测试
发现,必须写成"super.父类函数"的形式才能生成invoke-super指令。
(5)kInterface:对应invoke-interface相关指令,指调用接口中定义的函数。
以上枚举变量的解释可参考第3章最后所列举的谷歌官方文档。
下面代码中的GetMethodInvokeType是迭代器ClassDataItemIterator的成员函数,它将
返回当前所遍历的成员函数的调用方式(InvokeType)。注意,该函数返回kSuper的逻辑和官
方文档对kSuper的描述并不一致。按照该函数的逻辑,它永远也不可能返回kSuper。笔者在模拟
器上验证过这个问题,此处从未有返回过kSuper的情况。感兴趣的读者不妨做一番调研 */
InvokeType type = it->GetMethodInvokeType(*class_def);
//调用ClassLinker的ResolveMethod进行解析,下文将介绍此函数。
ArtMethod* method =
linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
*dex_file, method_idx, dex_cache,
class_loader, nullptr, type);
......
//调用另外一个VerifyMethod函数,其代码见下文
MethodVerifier::FailureData result = VerifyMethod(self,method_idx,...);
......
return failure_data;
}
MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
......) {
MethodVerifier::FailureData result;
/*创建一个MethodVerifier对象,然后调用它的Verify方法。其内部将校验method(类型为Art-
Method*)所代表的Java方法。该方法对应的字节码在code_item(对应dex文件格式里的code_
item)中。 */
MethodVerifier verifier(self,dex_file, dex_cache, class_loader,
......);
//Verify返回成功,表示校验通过。即使出现kSoftFailure的情况,该函数也会返回true
if (verifier.Verify()) {//main
......
//failures_的类型为vector<VerifyError>。VerifyError为枚举变量,定义了校验中可能
//出现的错误情况
if (verifier.failures_.size() != 0) { result.kind = kSoftFailure; }
if (method != nullptr) {.....}
} else {
/*Verify返回失败,但若错误原因是一些试验性指令导致的,则也属于软错误,Dex指令码中有
一些属于试验性质的指令,比如invokelambda。搜索dex_instruction.h文件里带kExperi-
mental标记的指令码,即是ART虚拟机所支持的试验性指令 */
if (UNLIKELY(verifier.have_pending_experimental_failure_)) {
result.kind = kSoftFailure;
} else { result.kind = kHardFailure;}
}
......
return result;
}
bool MethodVerifier::Verify() {
//从dex文件里取出该方法对应的method_id_item信息
const DexFile::MethodId& method_id =
dex_file_->GetMethodId(dex_method_idx_);
//取出该函数的函数名
const char* method_name = dex_file_->StringDataByIdx(method_id.name_idx_);
/*根据函数名判断其是类实例的构造函数还是类的静态构造函数。代码中,"<init>"叫类实例构造函数
(instance constructor),而"<clinit>"叫类的静态构造函数(static constructor)。 */
bool instance_constructor_by_name = strcmp("<init>", method_name) == 0;
bool static_constructor_by_name = strcmp("<clinit>", method_name) == 0;
//上述条件有一个为true,则该函数被认为是构造函数
bool constructor_by_name = instance_constructor_by_name ||
static_constructor_by_name;
/*如果该函数的访问标记(access flags,可参考第3章表3-1自己为构造函数,而函数名又不符合要
求,则设置校验的结果为VERIFY_ERROR_BAD_CLASS_HARD(VerifyError枚举值中的一种)。Fail
函数内部会处理VerifyError里定义的不同错误类型。其中以HARD结尾的枚举变量表示为硬错误 */
if ((method_access_flags_ & kAccConstructor) != 0) {
if (!constructor_by_name) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD)
<< "method is marked as constructor, but not named accordingly";
return false;
}
is_constructor_ = true;
} else if (constructor_by_name) { is_constructor_ = true; }
//code_item_代表该函数的内容,如果为nullptr,则表示这个函数为抽象函数或native函数
if (code_item_ == nullptr) {
//既不是抽象函数,也不是native函数,但又没有函数内容,校验肯定会失败
if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << ......; //错误原因;
return false;
}
return true;
}
/*参考3.2.4节可知,ins_size_表示输入参数所占虚拟寄存器的个数,而registers_size_表示该
函数所需虚拟寄存器的总个数。显然,下面这个if条件为true的话,这个函数肯定会校验失败 */
if (code_item_->ins_size_ > code_item_->registers_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << ......;
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << ......;
return false;
}
//insn_flags_将保存该方法里的指令码内容
insn_flags_.reset(arena_.AllocArray<InstructionFlags>(
code_item_->insns_size_in_code_units_));
std::uninitialized_fill_n(insn_flags_.get(),
code_item_->insns_size_in_code_units_,
InstructionFlags());
//下面四个函数将对指令码的内容进行校验。读者不拟介绍它们,感兴趣的读者不妨自行研究。
bool result = ComputeWidthsAndCountOps();
result = result && ScanTryCatchBlocks();
result = result && VerifyInstructions();
result = result && VerifyCodeFlow();
return result;
}
执行clinit方法
bool ClassLinker::EnsureInitialized(Thread* self,
Handle<mirror::Class> c,
bool can_init_fields,
bool can_init_parents) {
if (c->IsInitialized()) {
EnsureSkipAccessChecksMethods(c, image_pointer_size_);
return true;
}
// SubtypeCheckInfo::Initialized must happen-before any new-instance for that type.
const bool success = InitializeClass(self, c, can_init_fields, can_init_parents);
return success;
bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
bool can_init_statics, bool can_init_parents) {
...... //多个线程也可能同时触发目标类的初始化工作,如果这个类已经初始化了,则直接返回
//判断是否能初始化目标类。因为该函数可以在dex2oat编译进程中调用,在编译进程中,某些情况下
//无须初始化类。这部分内容我们不关注,读者以后碰到相关代码时可回顾此处的处理。
if (!CanWeInitializeClass(klass.Get(), can_init_statics,
can_init_parents)) { return false; }
{ //
.....
if (!klass->IsVerified()) { //如果类还没有校验,则校验它
VerifyClass(self, klass);
......
}
......
/*下面这个函数将对klass做一些检查,大体功能包括:
(1)如果klass是接口类,则直接返回,不做任何检查。
(2)如果klass和它的基类superclass是由两个不同的ClassLoader加载的,则需要对比检
查klass VTable和superclass VTable中对应项的两个ArtMethod是否有相同的签名
信息,即两个成员方法的返回值类型、输入参数的个数以及类型是否一致。
(3)如果klass有Iftable,则还需要检查klass IfTable中所实现的接口类的函数与对应
接口类里定义的接口函数是否有一样的签名信息。是否开展检查的前提条件也是klass和接
口类由不同的ClassLoader加载。如果检查失败,则会创建java.lang.LinkageError 错误信息。 */
if (!ValidateSuperClassDescriptors(klass)) {.....}
......
//设置执行类初始化操作的线程ID以及类状态为kStatusInitializing
klass->SetClinitThreadId(self->GetTid());
mirror::Class::SetStatus(klass,
mirror::Class::kStatusInitializing, self);
}
//根据JLS规范,klass如果是接口类的话,则不需要初始化接口类的基类(其实就是Object)
if (!klass->IsInterface() && klass->HasSuperClass()) {
mirror::Class* super_class = klass->GetSuperClass();
if (!super_class->IsInitialized()) {
......
Handle<mirror::Class> handle_scope_super(hs.NewHandle(super_class));
bool super_initialized = InitializeClass(self, handle_scope_super,
can_init_statics, true);
...... //基类初始化失败的处理
}
}
//初始化klass所实现的那些接口类
if (!klass->IsInterface()) {
size_t num_direct_interfaces = klass->NumDirectInterfaces();
if (UNLIKELY(num_direct_interfaces > 0)) {
MutableHandle<mirror::Class> handle_scope_iface(....);
for (size_t i = 0; i < num_direct_interfaces; i++) {
//handle_scope_iface代表一个接口类对象
handle_scope_iface.Assign(mirror::Class::GetDirectInterface(
self, klass, i));
//检查接口类对象是否设置了kAccRecursivelyInitialized标记位。这个标记位表示
//这个接口类已初始化过了。该标志位是ART虚拟机内部处理类初始化时的一种优化手段
if (handle_scope_iface->HasBeenRecursivelyInitialized()) {continue; }
//初始化接口类,并递归初始化接口类的父接口类
bool iface_initialized = InitializeDefaultInterfaceRecursive(self,
handle_scope_iface,......);
if (!iface_initialized) { return false; }
}
}
}
/*到此,klass的父类及接口类都已经初始化了。接下来要初始化klass中的静态成员变量。读者可回
顾图8-7 class_def结构体,其最后一个成员变量为static_values_off,它代表该类静态成员
变量初始值存储的位置。找到这个位置,即可取出对应静态成员变量的初值。 */
const size_t num_static_fields = klass->NumStaticFields();
if (num_static_fields > 0) {
//找到klass对应的ClassDef信息以及对应的DexFile对象
const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
const DexFile& dex_file = klass->GetDexFile();
StackHandleScope<3> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
klass->GetClassLoader()));
//找到对应的DexCache对象
Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
.....
//遍历ClassDef中代表static_values_off的区域
EncodedStaticFieldValueIterator value_it(dex_file, &dex_cache,
&class_loader, this, *dex_class_def);
const uint8_t* class_data = dex_file.GetClassData(*dex_class_def);
ClassDataItemIterator field_it(dex_file, class_data);
if (value_it.HasNext()) {
for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) {
//找到对应的ArtField成员。下文会介绍ResolveField函数
ArtField* field = ResolveField(dex_file, field_it.GetMemberIndex(),
dex_cache, class_loader, true);
//设置该ArtField的初值,内部将调用Class的SetFieldXXX相关函数,它会在Class
//对象中存储对应静态成员变量内容的位置(其值为ArtField的offset_)上设置初值。
value_it.ReadValueToField<...>(field);
}
}
}
//找到类的"<clinit>"函数,并执行它
ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
if (clinit != nullptr) {
JValue result;
clinit->Invoke(self, nullptr, 0, &result, "V");
}
bool success = true;
{
if (.....) {......}
else { //初始化正常
.....
//设置类状态为kStatusInitialized
mirror::Class::SetStatus(klass, mirror::Class::kStatusInitialized,self);
//下面这个函数设置klass静态成员方法ArtMethod的trampoline入口地址。它和
//Java方法的执行有关,这部分内容我们留待后文再来介绍。
//更新机器码入口地址entry_point_from_quick_compiled_code_
FixupStaticTrampolines(klass.Get());
}
}
return success;
}
图8-10 AbsClass0的情况
图8-11 ConcreteClass的情况
图8-12 ConcreteChildClass的情况
·methods_:仅保存在本类中定义的direct、virtual以及拷贝过来的方法。
·vtable_或embedded_vtable_:如果一个类是可实例化的,则只存在embedded_vtable_变量,否则只存在vtable_。这两个变量保存的信息是一样的,即这个类所有的virtual方法。这个表内容可能会非常多,因为它包含了来自整个继承和实现关系上的所有类的virtual方法。
·embedded_imtable_:如果一个类是可实例化的,则存在这个变量。它存储了接口类方法。embedded_imtable_本身起到快查表的作用,方便快速找到接口方法。
·iftable_:存储了该类在接口实现(可能是父类的接口实现关系)实现关系上的信息,包括继承了哪些接口类,实际的接口方法。
图8-14 reference_instance_offsets_说明
mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor,
mirror::Class* klass) {
// For temporary classes we must wait for them to be retired.
if (init_done_ && klass->IsTemp()) {
CHECK(!klass->IsResolved());
if (klass->IsErroneous()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) {
if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) {
return false;
}
if (quick_code == nullptr) {
return true;
}
......
}
// Return the address of quick stub code for bridging from quick code to the interpreter.
extern "C" void art_quick_to_interpreter_bridge(ArtMethod*);
static inline const void* GetQuickToInterpreterBridge() {
return reinterpret_cast<const void*>(art_quick_to_interpreter_bridge);
}
void ClassLinker::ThrowEarlierClassFailure(ObjPtr<mirror::Class> c, bool wrap_in_no_class_def) {
// The class failed to initialize on a previous attempt, so we want to throw
// a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
// failed in verification, in which case v2 5.4.1 says we need to re-throw
// the previous error.
Runtime* const runtime = Runtime::Current();
if (!runtime->IsAotCompiler()) { // Give info if this occurs at runtime.
std::string extra;
if (GetVerifyError(c) != nullptr) {
ObjPtr<mirror::Object> verify_error = GetVerifyError(c);
if (verify_error->IsClass()) {
extra = mirror::Class::PrettyDescriptor(verify_error->AsClass());
} else {
extra = verify_error->AsThrowable()->Dump();
}
}
LOG(INFO) << "Rejecting re-init on previously-failed class " << c->PrettyClass()
<< ": " << extra;//the stack log of NoClassDefFoundError came from here
}
static mirror::Object* GetVerifyError(ObjPtr<mirror::Class> c)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::ClassExt> ext(c->GetExtData());
if (ext == nullptr) {
return nullptr;
} else {
return ext->GetVerifyError();
}
}
art/runtime/mirror/class-inl.h
HeapReference<ClassExt> ext_data_;
template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline ClassExt* Class::GetExtData() {
return GetFieldObject<ClassExt, kVerifyFlags, kReadBarrierOption>(
OFFSET_OF_OBJECT_MEMBER(Class, ext_data_));
}
art/runtime/mirror/class_ext.h
// C++ mirror of dalvik.system.ClassExt
class MANAGED ClassExt : public Object {
// The saved verification error of this class.
HeapReference<Object> verify_error_;
Object* GetVerifyError() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldObject<ClassExt>(OFFSET_OF_OBJECT_MEMBER(ClassExt, verify_error_));
}
}
art/runtime/oat_file-inl.h
inline const void* OatFile::OatMethod::GetQuickCode() const {
return GetOatPointer<const void*>(GetCodeOffset());
}
inline uint32_t OatFile::OatMethod::GetCodeOffset() const {
return (GetQuickCodeSize() == 0) ? 0 : code_offset_;
}
art/runtime/runtime.h
// IsAotCompiler for compilers that don't have a running runtime. Only dex2oat currently.
bool IsAotCompiler() const {
return !UseJitCompilation() && IsCompiler();
}
// IsCompiler is any runtime which has a running compiler, either dex2oat or JIT.
bool IsCompiler() const {
return compiler_callbacks_ != nullptr;
}
art/runtime/thread.cc
void Thread::ThrowNewWrappedException(const char* exception_class_descriptor,
const char* msg) {
DCHECK(!runtime->IsStarted() || exception_class->IsThrowableClass());
Handle<mirror::Throwable> exception(
hs.NewHandle(ObjPtr<mirror::Throwable>::DownCast(exception_class->AllocObject(this))));
ArtMethod* exception_init_method =
exception_class->FindConstructor(signature, cl->GetImagePointerSize());
InvokeWithJValues(soa, ref.get(), exception_init_method, jv_args);
if (LIKELY(!IsExceptionPending())) {
SetException(exception.Get());
}
void Thread::SetException(ObjPtr<mirror::Throwable> new_exception) {
CHECK(new_exception != nullptr);
// TODO: DCHECK(!IsExceptionPending());
tlsPtr_.exception = new_exception.Ptr();
}
libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException(
"Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}