虚拟机调用java类的成员函数时,如果类还没有加载,要先加载类,对这个类做一些处理,直到所有信息都准备好后,才能被使用。
- 加载:其实就是把类信息从dex文件提取到虚拟机内部。(也可能是从jar、oat)
- 链接:可以细分为校验、准备和解析。校验合法性,类格式是否正确、字节码是否合法等;Prepare指为类信息分配一块存储空间;Resolve,如果类成员引用其他类的话,可能需要把其他类也加载到虚拟机。
- 初始化:初始化静态成员变量值,执行static语句块等。
何时会触发类的加载链接流程?
ART中处理类的加载和链接工作的入口函数是DefineClass,类的初始化函数则只有在创建这个类的对象或者操作这个类的成员方法或变量是才会触发。
DefineClass在很多地方都可能被触发,如下:
- FindClass:根据类的字符串名搜索一个类。如果没有的话,可能触发类的加载和链接。
- Resolve相关函数:名字虽然叫Resolve,但不是类加载、链接和初始化过程中提到的Resolve,它可能触发类的加载链接。ClassLinker::ResolveType、ResolveMethod、ResolveString、ResolveField。
以FindClass为例,调用流程如下:
前言:基础类型介绍(本篇基于android9)
ArtField和ArtMethod分别代表类的成员变量和成员方法的数据结构。
class ArtField {
GcRoot declaring_class_;//在哪个类中被定义
uint32_t access_flags_ = 0;//访问标记
uint32_t field_dex_idx_ = 0;//dex文件中field_ids数组中的索引
uint32_t offset_ = 0;//成员变量的offset,ArtField本身并不提供变量所需空间,指向地址
};
class ArtMethod {
GcRoot declaring_class_;
std::atomic access_flags_;
uint32_t dex_code_item_offset_;
uint32_t dex_method_index_;
uint16_t method_index_; //与class类管理成员函数有关
uint16_t hotness_count_;//热度,与JIT有关
struct PtrSizedFields {
void* data_;//与JNI有关
void* entry_point_from_quick_compiled_code_;//java方法入口函数地址
} ptr_sized_fields_;
};
Dex文件按自己的格式存储type_ids、string_ids、field_ids和method_ids等,基本都借助symbol reference来间接获取信息。相反,DexCache则是直接包含最终信息。
class DexCache : public Object {
HeapReference location_;//dex文件对应的路径
uint64_t dex_file_; // DexFile*,指向关联的dex文件
/* ArtField**指向ArtField*数组,成员类型为ArtField*。改数组存储了一个dex文件中定义的所有类的成
员变量。另外,只有那些解析后的ArtField对象才会存到这个数组里。该字段和dex文件里的field_ids
数组有关。 */
uint64_t resolved_fields_;
/* ArtMethod**指向ArtMethod*数组,成员类型为ArtMethod*。改数组存储了一个dex文件中定义的所
有类的成员变量。另外,只有那些解析后的ArtMethod对象才会存到这个数组里。该字段和dex文件里
的method_ids数组有关。 */
uint64_t resolved_methods_;
/*实际为GcRoot*,指向GcRoot数组,成员类型为GcRoot(本质上是
mirror::Class*)。它存储该dex文件里使用的数据类型信息数组。该字段和dex文件里的type_ids数组有
关。 */
uint64_t resolved_types_; // 例如:Ljava/lang/String
/*实际为GcRoot*,指向GcRoot数组,成员类型为GcRoot(本质上是
mirror::String*)。它存储该dex文件里使用的字符串信息数组。该字段和dex文件里的string_ids数组
有关。 */
uint64_t strings_;
//上述四个数组的长度
uint32_t num_resolved_fields_;
uint32_t num_resolved_methods_;
uint32_t num_resolved_types_;
uint32_t num_strings_;
};
Class成员变量非常多,如下几个尤其值得注意:
iftable_:保存该类直接实现或间接实现的接口信息。直接实现是指该类自己implements的某个接口,间接实现是指它的继承关系树上有某个祖父类implements了某个接口。另外,一条接口信息包含两个部分,第一部分是接口类所对应的Class对象,第二部分则是该接口类中的方法。
vtabl_:保存了所有直接定义或间接定义的virtual方法。比如Object类中的wait、notify、toString等方法。
methods_:methods_只包含本类直接定义的direct、virtual方法和拷贝过来的诸如Miranda这样的方法。一般vtable_包含内容远多于methods。
embedded_vtable_:隐含成员变量。能实例化的class,vtable_=nullptr,embedded_vtable_代替;embedded_imtable_包含64个元素,只存储virtual方法中属于接口的方法,类似于快表。索引为dexIndex%kImtSize取模。
direct_methods:该类中static、private、构造函数.
virtual_methods:除direct之外的,并且不包括从父类继承的函数(没有重载它的话).
enum class ClassStatus : uint8_t {
kNotReady = 0,
kRetired = 1,
kErrorResolved = 2,
kErrorUnresolved = 3,
kIdx = 4,
kLoaded = 5,
kResolving = 6,
kResolved = 7,
kVerifying = 8,
kRetryVerificationAtRuntime = 9,
kVerifyingAtRuntime = 10,
kVerified = 11,
kSuperclassValidated = 12,
kInitializing = 13,
kInitialized = 14,
kLast = kInitialized
};
class Class : public Object {
HeapReference class_loader_;//加载本类的ClassLoader对象。如果为空,则为bootstrap system loader
HeapReference dex_cache_;//该类缓存在哪个DexCache对象中。
HeapReference iftable_;//interface信息表
HeapReference name_;//类名
HeapReference super_class_;//父类
HeapReference vtable_;//虚函数表
/* [0,virtual_methods_offset_):本类的direct函数
[virtual_methods_offset_,copied_methods_offset_):本类的virtual函数
[copied_methods_offset_, ...)诸如miranda函数等 */
uint64_t methods_;
uint16_t virtual_methods_offset_;
uint16_t copied_methods_offset_;
uint64_t sfields_;//本类的静态成员变量
uint32_t access_flags_;//访问标志
uint32_t class_flags_;//虚拟机内部使用
uint32_t class_size_;//当分配一个对象时,用于说明这个类对象所需内存大小
int32_t dex_class_def_idx_;//本类在dex文件中class_defs数组对应的索引
int32_t dex_type_idx_;//本类在dex文件里type_ids中的索引
uint32_t num_reference_instance_fields_;//引用类型的非静态成员变量个数
uint32_t num_reference_static_fields_;//引用类型的静态成员变量个数
uint32_t object_size_;//实例所占内存大小。也就是new一个实例时所需内存大小
uint32_t status_;//类状态
static GcRoot java_lang_Class_;//指向代表java/lang/Class的类对象
//下面三个变量虽然在注释语句,但实际的Class对象内存空间可能包含对应内容
// ImTableEntry embedded_imtable_[0];
// VTableEntry embedded_vtable_[0];
// uint32_t fields_[0];
};
加载链接详细流程:
核心功能都由DefineClass()承担,如下按加载链接流程分段介绍,代码部分进行了删减,只保留关键代码:
1.加载(load)
ClassLinker::SetupClass
首先SetupClass把类的状态从kStatusNotReady切换为kStatusIdx,并为传入的klass对象设置一些最基本信息。
void ClassLinker::SetupClass(const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle klass,
ObjPtr class_loader) {
//SetClass是Class的基类mirror Object中的函数。
//Class也是一种Object,所以此处设置它的类类型为"java/lang/Class"对应的那个Class对象。
klass->SetClass(GetClassRoot(kJavaLangClass));
//设置访问标志及该类的加载器对象
const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
klass->SetAccessFlags(access_flags);
klass->SetClassLoader(class_loader);
//设置klass的状态为kIdx
mirror::Class::SetStatus(klass, ClassStatus::kIdx, nullptr);
//设置klass的dex_class_def_idx_和dex_type_idx_成员变量
klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
klass->SetDexTypeIndex(dex_class_def.class_idx_);
}
ClassLinker::LoadClass
LoadClass()首先从dex中提取class_data信息,然后调用LoadClassMembers()加载静态、非静态成员变量,direct、virtual函数等信息到klass。
void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, Handle klass) {
const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
LoadClassMembers(self, dex_file, class_data, klass);
}
void ClassLinker::LoadClassMembers(Thread* self,
const DexFile& dex_file,
const uint8_t* class_data,
Handle klass) {
// Load static fields.
LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader());
//创建class_data_item迭代器
ClassDataItemIterator it(dex_file, class_data);
//分配存储类静态成员变量的固定长度数组sfields
LengthPrefixedArray* sfields = AllocArtFieldArray(self,
allocator,
it.NumStaticFields());
//遍历class_data_item中的静态成员变量数组,然后填充到sfields
for (; it.HasNextStaticField(); it.Next()) {
LoadField(it, klass, &sfields->At(num_sfields));
}
// Load instance fields.
//同理,填充非静态成员变量
LengthPrefixedArray* ifields = AllocArtFieldArray(self,
allocator,
it.NumInstanceFields());
for (; it.HasNextInstanceField(); it.Next()) {
LoadField(it, klass, &ifields->At(num_ifields));
}
// Load methods.
//是否关联有oat class,LinkCode使用,设置ArtMethod入口函数地址
const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr;
//设置Class类的methods_成员变量,包含direct、virtual函数
klass->SetMethodsPtr(AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()),
it.NumDirectMethods(),
it.NumVirtualMethods());
//遍历direct,加载它们然后关联字节码
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);
LoadMethod(dex_file, it, klass, method);
LinkCode(this, method, oat_class_ptr, class_def_method_index);
//设置method_index_,该值就是这个ArtMethod位于klass methods_数组中的位置
uint32_t it_method_index = it.GetMemberIndex();
method->SetMethodIndex(class_def_method_index);
}
//virtual方法,它们的method_index_和methods_数组没有关系,不用调用SetMethodIndex
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
LoadMethod(dex_file, it, klass, method);
LinkCode(this, method, oat_class_ptr, class_def_method_index);
}
}
ClassLinker::LoadField
void ClassLinker::LoadField(const ClassDataItemIterator& it,
Handle klass,
ArtField* dst) {
const uint32_t field_idx = it.GetMemberIndex();
dst->SetDexFieldIndex(field_idx);//设置对应dex文件里的field_idx
dst->SetDeclaringClass(klass.Get());//由哪个Class对象定义
// 9.0 hidden API标志
uint32_t access_flags = it.GetFieldAccessFlags();
if (klass->IsBootStrapClassLoaded()) {
access_flags =
HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags());
}
dst->SetAccessFlags(access_flags);//访问标志
}
ClassLinker::LoadMethod
void ClassLinker::LoadMethod(const DexFile& dex_file,
const ClassDataItemIterator& it,
Handle 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);//设置dex_method_idx
dst->SetDeclaringClass(klass.Get());//设置declaring_class_
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());//设置dex_code_item_offset_
uint32_t access_flags = it.GetMethodAccessFlags();//设置访问标志
//9.0 hidden API标志
if (klass->IsBootStrapClassLoaded()) {
access_flags =
HiddenApiAccessFlags::EncodeForRuntime(access_flags, it.DecodeHiddenAccessFlags());
}
//如果函数名为"finalize",设置该类为finalizable
if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) {
if (klass->GetClassLoader() != nullptr) {
klass->SetFinalizable();
} else {...}
}
} else if (method_name[0] == '<') {
......//如果函数名为""或"",设置访问标志kAccConstructor
}
}
dst->SetAccessFlags(access_flags);
}
另外,如果存在基类或实现了接口类,会调用ClassLinker::LoadSuperAndInterfaces去找,主要调用ClassLinker::ResolveType去解析,内部又调用FindClass,如果类不存在,则会触发目标类的加载和链接。
2.链接(link)
经过上述的load过程,类的基本信息已经从dex文件提取出来并转化成了mirror Class对象,LinkCass最主要是解析和填充iftable_、vtable_等。
bool ClassLinker::LinkClass(Thread* self,
const char* descriptor,
Handle klass,
Handle> interfaces,
MutableHandle* h_new_class_out) {
//根据父类的类属性,设置本类的一些属性
LinkSuperClass(klass);
//Interface Method Table的缩写,提供接口方法的快表查询
ArtMethod* imt_data[ImTable::kSize];
//对该类包含的方法进行处理。(包括它实现的接口、继承自父类的方法等)
LinkMethods(self, klass, interfaces, &new_conflict, imt_data);
//对成员变量进行处理
LinkInstanceFields(self, klass);
LinkStaticFields(self, klass, &class_size);
//处理Class的reference_instance_offsets_成员变量 TODO
CreateReferenceInstanceOffsets(klass);
if (!klass->IsTemp() || (!init_done_ && klass->GetClassSize() == class_size)) {
//当目标类是基础数据类、抽象类(不包括数组)、接口类时,不需要额外操作
//将klass状态设置为kResolved,然后赋值给h_new_class_out即可
mirror::Class::SetStatus(klass, ClassStatus::kResolved, self);
h_new_class_out->Assign(klass.Get());
} else {
StackHandleScope<1> hs(self);
//CopyOf是关键,先创建一个大小为class_size的Class对象,然后将信息拷贝到这个新Class中。
auto h_new_class = hs.NewHandle(klass->CopyOf(self, class_size, imt, image_pointer_size_));
//清理klass内容
klass->SetMethodsPtrUnchecked(nullptr, 0, 0);
klass->SetSFieldsPtrUnchecked(nullptr);
klass->SetIFieldsPtrUnchecked(nullptr);
{
//更新ClassTable中对应的信息
ObjPtr existing = table->UpdateClass(descriptor, h_new_class.Get(),
ComputeModifiedUtf8Hash(descriptor));
}
//设置klass状态为kRetired,表示已被废弃
mirror::Class::SetStatus(klass, ClassStatus::kRetired, self);
//设置新类状态为kResolved,表示解析完毕
mirror::Class::SetStatus(h_new_class, ClassStatus::kResolved, self);
h_new_class_out->Assign(h_new_class.Get());
}
return true;
}
ClassLinker::LinkMethods
LinkMethods()非常复杂,通过一个实际的代码例子和图解来理解linkmethod。
- methods_:仅保存在本类中定义的direct、virtual以及拷贝过来的方法
- vtable_或embedded_vtable_:如果类可实例化,则只存在embedded_vtable_,否则vtable_。
- embedded_imtable_:如果类可实例化,则存在这个变量。方便快速找到接口方法。
- iftable_:存储该类在接口实现关系上的信息,包括继承了哪些接口类,实际的接口方法。
method_index_:
- 如果这个ArtMethod对应的是一个static或direct函数,则method_index_是指向定义它的类的methods_中的索引。
- 如果这个ArtMethod是virtual函数,则method_index_是指向它的VTable中的索引。注意,可能多个类的VTable都包含该ArtMethod对象(比如object的那11个方法),所以要保证这个method_index_在不同VTable中都有相同的值---这也是LinkMethods中那三个函数比较复杂的原因。
public interface If0{
public void doInterfaceWork0();
public void doInterfaceWork1();
}
public static interface If1 extends If0{
public void doInterfaceWork2();
public void doInterfaceWork3();
}
public static abstract class AbsClass0 implements If1{
public void doInterfaceWork0(){return;}
public void doInterfaceWork3(){return;}
abstract public void doAbsWork0();
public void doRealWork0(){return;}
}
public static class ConcreteClass extends AbsClass0{
public void doInterfaceWork0(){return;}
public void doInterfaceWork1(){return;}
public void doInterfaceWork2(){return;}
public void doInterfaceWork3(){return;}
public void doAbsWork0(){return;}
public void doRealWork1(){return;}
}
public static class ConcreteChildClass extends ConcreteClass{
public void doRealWork2(){return;}
}
ClassLinker::LinkFields
LinkInstanceFields、LinkStaticFields都调用LinkFields,核心就是计算class大小、设置ArtField的offset_变量。完成之后的内存布局如下,不再详细解释,自行阅读ClassLinker::LinkFields、Class::ComputeClassSize相关代码。
ClassLinker::CreateReferenceInstanceOffsets
设置Class类的reference_instance_offsets_,一个32位的bitmap,提供了一种快速访问类非静态引用变量的方法。
reference_instance_offsets_如果某个位上为1,说明对应的位置上有一个非静态引用类型变量,否则为0。如果超过32个非静态引用对象,放弃优化,沿着派生关系向上对引用型变量查找。
void ClassLinker::CreateReferenceInstanceOffsets(Handle klass) {
uint32_t reference_offsets = 0;
ObjPtr super_class = klass->GetSuperClass();
if (super_class != nullptr) {
reference_offsets = super_class->GetReferenceInstanceOffsets();
//kClassWalkSuper是一个特殊标志,表示不使用优化
if (reference_offsets != mirror::Class::kClassWalkSuper) {
//本类包含的非静态引用类型变量个数
size_t num_reference_fields = klass->NumReferenceInstanceFieldsDuringLinking();
if (num_reference_fields != 0u) {
//计算起始位置
uint32_t start_offset = RoundUp(super_class->GetObjectSize(),
sizeof(mirror::HeapReference));
uint32_t start_bit = (start_offset - mirror::kObjectHeaderSize) /
sizeof(mirror::HeapReference);
//剩余bit位不足,则放弃优化
if (start_bit + num_reference_fields > 32) {
reference_offsets = mirror::Class::kClassWalkSuper;
} else {
//reference_offsets初值为父类比特位,或上本类
reference_offsets |= (0xffffffffu << start_bit) &
(0xffffffffu >> (32 - (start_bit + num_reference_fields)));
}
}
}
}
klass->SetReferenceInstanceOffsets(reference_offsets);
}
接着看一段使用reference_instance_offsets_的代码Object::VisitFieldsReferences。
template
inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& visitor) {
//非静态,并且≠kClassWalkSuper
if (!kIsStatic && (ref_offsets != mirror::Class::kClassWalkSuper)) {
uint32_t field_offset = mirror::kObjectHeaderSize;
while (ref_offsets != 0) {
if ((ref_offsets & 1) != 0) {
//如果某位比特位值为1,则指向一个引用类型变量
visitor(this, MemberOffset(field_offset), kIsStatic);
}
ref_offsets >>= 1;
//调整field_offset的位置
field_offset += sizeof(mirror::HeapReference);
}
} else {
//如下代码删除了kIsStatic相关,可以看到先遍历本类变量,再向上变量super类变量
for (ObjPtr klass = GetClass(); klass != nullptr; klass = klass->GetSuperClass()) {
const size_t num_reference_fields = klass->NumReferenceInstanceFields();
MemberOffset field_offset = klass->GetFirstReferenceInstanceFieldOffset();
for (size_t i = 0u; i < num_reference_fields; ++i) {
if (field_offset.Uint32Value() != ClassOffset().Uint32Value()) {
visitor(this, field_offset, kIsStatic);
}
field_offset = MemberOffset(field_offset.Uint32Value() +
sizeof(mirror::HeapReference));
}
}
}
}
3.初始化(initialize)
ART虚拟机并没有严格按照java规范来实施加载类的流程,Verify会在initialize阶段触发,dex2oat阶段也有Verify动作。Verify主要针对java方法,成员变量无校验之说。Verify跟java规范关系很大,繁杂琐碎,本篇不打算介绍细节,只关注大概流程即可。详细可自行从ClassLinker::VerifyClass()进行追踪。
Verify:
ClassLinker::VerifyClass()中会判断预校验的状态:
bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status);
- 预校验:verify可以在dex2oat阶段进行,如果校验成功,则类状态设为kVerified。如果出现校验软错误,则类状态设置为kRetryVerificationAtRuntime,之后在完整runtime运行时再校验。
- 状态为kVerified:完整runtime运行时会为该类中的methods_数组里的所有ArtMethod对象设置kAccSkipAccessChecks标志,同时也为类设置kAccVerificationAttempted标记(表示类已经校验过,不需要再做校验)。
- 类状为kRetryVerificationAtRuntime:完整runtime运行时再校验。
ClassLinker::InitializeClass
InitializeClass执行完毕会设置类状态为kInitialized,至此类已完全准备好。
bool ClassLinker::InitializeClass(Thread* self, Handle klass,
bool can_init_statics, bool can_init_parents) {
//如果类还没有校验,则校验它
if (!klass->IsVerified()) {
VerifyClass(self, klass);
......
}
//初始化klass所实现的接口
if (!klass->IsInterface()) {
size_t num_direct_interfaces = klass->NumDirectInterfaces();
if (UNLIKELY(num_direct_interfaces > 0)) {
StackHandleScope<1> hs_iface(self);
MutableHandle handle_scope_iface(hs_iface.NewHandle(nullptr));
for (size_t i = 0; i < num_direct_interfaces; i++) {
handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass.Get(), i));
// 初始化接口类,并递归初始化接口类的父接口类
bool iface_initialized = InitializeDefaultInterfaceRecursive(self, handle_scope_iface,
can_init_statics, can_init_parents);
}
}
}
//初始化klass中的静态成员变量。class_def结构体的static_values_off代表该类静态成员变量初始值存储位置。
const size_t num_static_fields = klass->NumStaticFields();
if (num_static_fields > 0) {
//找到klass对应的ClassDef信息
const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
StackHandleScope<3> hs(self);
Handle class_loader(hs.NewHandle(klass->GetClassLoader()));
//找到对应的DexCache对象
Handle dex_cache(hs.NewHandle(klass->GetDexCache()));
......
//遍历ClassDef中代表Static_values_off
annotations::RuntimeEncodedStaticFieldValueIterator value_it(dex_cache,
class_loader,
this,
*dex_class_def);
const DexFile& dex_file = *dex_cache->GetDexFile();
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成员
ArtField* field = ResolveField(field_it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true);
//设置ArtField的初值,在ArtField的offset_上设置初值。
value_it.ReadValueToField(field);
}
}
}
//找到类的""函数,并执行它
if (!self->IsExceptionPending()) {
ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
if (clinit != nullptr) {
JValue result;
clinit->Invoke(self, nullptr, 0, &result, "V");
}
}
......
//设置类状态为kInitialized,至此类已完全准备好
mirror::Class::SetStatus(klass, ClassStatus::kInitialized, self);
//静态成员静态方法在linkcode时会设置trampoline入口地址,初始化完成后设置真正的入口地址
FixupStaticTrampolines(klass.Get());
......
return success;
}
参考资料
- 邓凡平大神的《深入理解Android Java虚拟机ART》
- 老罗的Android之旅虚拟机相关博客