一个ArtField对象仅仅是代表一个Java类的成员变量,但它自己并不提供空间来存储这个Java成员变量的内容。Class LinkFields时我们将看到这个Java成员变量所需的存储空间在什么地方
....
private:
GcRoot<mirror::Class> declaring_class_; //该成员变量在哪个类中被定义
uint32_t access_flags_; //该成员变量的访问标记
//该成员变量在dex文件中field_ids数组中的索引,注意,它是由图8-7中encoded_field结
//构体中field_idx_diff计算而来
uint32_t field_dex_idx_;
//如果ArtField所代表的成员变量是类的静态成员变量,则下面的offset_代表是该变量实际的存储
//空间在图8-13里Class内存布局中的起始位置。如果是非静态成员变量,则offset_指向图8-13中
//Object内存布局里对应的位置。
uint32_t offset_;
7.0
......
protected:
//下面这四个成员变量的解释可参考图8-7
GcRoot<mirror::Class> declaring_class_; //本函数在哪个类中声明
uint32_t access_flags_;
uint32_t dex_code_item_offset_;
//表示某个方法在dex文件method_ids数组中的索引
uint32_t dex_method_index_;
//与ArtField的field_index_类似,下面这个成员变量和Class类如何管理它的成员函数有关。
//如果这个ArtMethod对应的是一个static或direct函数,则method_index_是指向定义它的类的methods_中的索引。
//如果这个ArtMethod是virtual函数,则method_index_是指向它的VTable中的索引。注意,可能多个类的VTable都包含该//ArtMethod对象(比如Object的那11个方法),所以要保证这个method_index_在不同VTable中都有相同的值——这也是//LinkMethods中那三个函数比较复杂的原因。
uint16_t method_index_;
//热度。函数每被调用一次,该值递增1。一旦超过某个阈值,该函数可能就需要被编译成本地方法以加
//快执行速度了。
uint16_t hotness_count_;
struct PACKED(4) PtrSizedFields {
//指向declaring_class_->dex_cache_的resolved_methods_成员,详情需结合下文对Dex-Cache的介绍。
ArtMethod** dex_cache_resolved_methods_;
//指针的指针,指向declaring_class_->dex_cache_的dex_cache_resolved_types_成员,详情需结合下文对DexCache的介绍
GcRoot<mirror::Class>* dex_cache_resolved_types_;
//下面两个变量是函数指针,它们是一个ArtMethod对象代表的Java方法的入口函数地址。
//我们后续章节介绍Java代码执行的时候再来讨论它
void* entry_point_from_jni_;
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
// the interpreter.
void* entry_point_from_quick_compiled_code_;
} ptr_sized_fields_;
}
9.0上的差异
// Must be the last fields in the method.
struct PtrSizedFields {
// Depending on the method type, the data is
// - native method: pointer to the JNI function registered to this method
// or a function to resolve the JNI function,
// - conflict method: ImtConflictTable,
// - abstract/interface method: the single-implementation if any,
// - proxy method: the original interface method or constructor,
// - other methods: the profiling data.
void* data_;
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
// the interpreter.
void* entry_point_from_quick_compiled_code_;
} ptr_sized_fields_;
.....
private:
HeapReference<Object> dex_;
//dex文件对应的路径
HeapReference<String> location_;
//实际为DexFile*,指向所关联的那个Dex文件。
uint64_t dex_file_;
/*实际为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<Class>*,指向GcRoot<Class>数组,成员的数据类型为GcRoot<Class>(本质
质上就是mirror::Class*),它存储的内容直接指向dex文件里用到的或自定义数据类型所对应的Class对象。它存储该dex文件里使用的数据类型信息数组。该字段和Dex文件里的type_ids数组有关。 */
uint64_t resolved_types_;
/*实际为GCRoot<String>*,指向GcRoot<String>数组,包括该dex文件里使用的字符串信息数组。
注意,GcRoot<String>本质上就是mirror::String*。该字段和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_;
};
public:
/*下面这个枚举变量用于描述类的状态。上文曾介绍过,一个类从dex文件里被加载到最终能被使
用将经历很多个操作步骤。这些操作并不是连续执行的,而是可能被分散在不同的地方以不同的时
机来执行不同的操作。所以,需要过类的状态来描述某个类当前处于什么阶段,这样便可知道下一
步需要做什么工作。Class对象创建之初,其状态为kStatusNotReady,最终可正常使用的状
态为kStatusInitialized。下文分析类加载的相关代码时,读者可看到状态是如何转变的。 */
enum Status {
kStatusRetired = -2, kStatusError = -1, kStatusNotReady = 0,
kStatusIdx = 1, kStatusLoaded = 2, kStatusResolving = 3,
kStatusResolved = 4, kStatusVerifying = 5,
kStatusRetryVerificationAtRuntime = 6,
kStatusVerifyingAtRuntime = 7, kStatusVerified = 8,
kStatusInitializing = 9, kStatusInitialized = 10,
kStatusMax = 11,
};
//加载本类的ClassLoader对象,如果为空,则为bootstrap system loader
HeapReference<ClassLoader> class_loader_;
//下面这个成员变量对数组类才有意义,用于表示数组元素的类型。比如,对String[][][]类而
//言,component_type_代表String[][]。本章后文介绍数组类的时候还会讨论它。
HeapReference<Class> component_type_;
//该类缓存在哪个DexCahce对象中。注意,有些类是由虚拟机直接创建的,而不是从Dex文件里
//读取的。比如基础数据类型。这种情况下dex_cache_取值为空。
HeapReference<DexCache> dex_cache_;
/*结合图8-6可知,IfTable从ObjectArray<Object>派生,所以它实际上是一个数组容器。
为什么不直接使用它的父类ObjectArray<Object>呢?根据ART虚拟机的设计,IfTable中
的一个索引位置其实包含两项内容,第一项是该类所实现的接口类的Class对象,第二项则是
和第一项接口类有关的接口函数信息。笔者先用伪代码来描述IfTable中索引x对应的内容:
第一项内容:具体位置为iftable_内部数组[x+0],元素类型为Class*,代表某个接口类
第二项内容:具体位置为iftable_内部数组[x+1],元素类型为PointArray*。如图8-6可知,
PointArray也是一个数组。其具体内容我们下文再详述。
另外,对类A而言,它的iftable_所包含的信息来自于如下三个方面:
(1)类A自己所实现的接口类。
(2)类A从父类(direct superclass)那里获取的信息。
(3)类A从接口父类(direct super interface)那里获取的信息。
笔者先不介绍上面所谓的信息具体是什么,下文将对IfTable的元素构成做详细代码分析。 */
HeapReference<IfTable> iftable_;
//本类的类名
HeapReference<String> name_;
//代表父类。如果本类代表Object或基础数据类型,则该成员变量为空
HeapReference<Class> super_class_;
/*virtual methods table。它指向一个PointArray数组,元素的类型为ArtMethod*。
这个vtable_的内容很丰富,下面的章节会详细介绍它。 */
HeapReference<PointerArray> vtable_;
//类的访问标志。该字段的低16位可虚拟机自行定义使用
uint32_t access_flags_;
uint64_t dex_cache_strings_;
//指向DexCache的strings_成员变量实际为LengthPrefixedArray<ArtField>,代表本
//类声明的非静态成员变量。注意,这个LengthPrefixedArray的元素类型是ArtField,不
//是ArtField*。
uint64_t ifields_;
/*下面这三个变量需配合使用。其中,methods_实际为LengthPrefixedArray<ArtMethod>,
代表该类自己定义的成员函数。它包括类里定义的virtual和direct的成员函数,也包括从接
口类中继承得到的默认函数以及所谓的miranda函数(下文将介绍接口类的默认实现函数以及
miranda函数)。methods_中元素排列如下:
(1)[0,virtual_methods_offset_)为本类包含的direct成员函数
(2)[virtual_methods_offset_,copied_methods_offset_)为本类包含的virtual
成员函数
(3)[copied_methods_offset_,...)为剩下的诸如miranda函数等内容 */
uint64_t methods_;
uint16_t copied_methods_offset_;
uint16_t virtual_methods_offset_;
uint64_t sfields_; //同ifields_类似,只不过保存的是本类的静态成员变量
uint32_t class_flags_; //虚拟机内部使用
uint32_t class_size_; //当分配一个类对象时,用于说明这个类对象所需的内存大小
pid_t clinit_thread_id_; //代表执行该类初始化函数的线程ID
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_;
//该类的实例所占据的内存大小。也就是我们在Java层new一个该类的实例时,这个实例所需的内存大小
uint32_t object_size_;
/*下面这个变量的低16位存储的是Primitive::Type枚举值,其定义如下:
enum Type { kPrimNot = 0, kPrimBoolean, kPrimByte, kPrimChar, kPrimShort,
kPrimInt, kPrimLong, kPrimFloat, kPrimDouble, kPrimVoid, kPrimInt, kPrimLong, kPrimFloat, kPrimDouble, kPrimVoid, kPrimLast = kPrimVoid }; 其中,kPrimNot表示非基础数据类型,即它代表引用类型。
primitive_type_的高16位另有作用,后文碰到再述 */
uint32_t primitive_type_;
//下面这个变量指向一个内存地址,该地址中存储的是一个位图,它和Class中用于表示引用类型
//的非静态成员变量的信息(ifields)有关。
uint32_t reference_instance_offsets_;
Status status_; //类的状态
/*特别注意。虽然下面三个成员变量定义在注释语句中,但实际的Class对象内存空间可能包含
对应的内容,笔者称之为Class的隐含成员变量。它们的取值情况我们下文会详细介绍*/
/*Embedded Imtable(内嵌Interface Method Table),是一个固定大小的数组。数组元素
的类型为ImTableEntry,但代码中并不存在这样的数据类型。实际上,下面这个隐含成员变量
的声明可用 ArtMethod* embedded_imtable_[0]来表示 */
//ImTableEntry embedded_imtable_[0];
/*Embedded Vtable(内嵌Virtual Table),是一个非固定大小的数组。数组元素为VTable-
Entry,但代码中也不存在这样的数据类型。和上面的embedded_imtable_类似,它的声明
也可用ArtMethod* embedded_vtable_[0]来表示 */。
//VTableEntry embedded_vtable_[0];
//下面这个数组存储Class中的静态成员变量的信息
//uint32_t fields_[0];
//再次请读者注意,以上三个隐含成员变量的内容将在下文介绍。
//指向代表java/lang/Class的类对象。注意,它是static类型,它不是隐含成员变量
static GcRoot<Class> java_lang_Class_;
};