1. 源文件
public class ClassLoadMechanism implements Serializable {
private static int STATE_NON_FINAL = 10;
private static final int STATIC_BASIC = 10;
private static final String STATIC_STR = "123";
private final int mLoading;
private final String mRef;
ClassLoadMechanism(int loading, String ref) {
this.mLoading = loading;
this.mRef = ref;
}
public void test() {
System.out.println("instance fun be called!");
}
public static void staticFun(int arg) {
System.out.println("static fun be called!");
}
}
2. 反编译源代码
javap -v -s -constants -p file:/classes/com/study/ClassLoadMechanism.class
2.1 反编译描述信息
Classfile /classes/com/yq/study/ClassLoadMechanism.class
Last modified Feb 27, 2021; size 1033 bytes
MD5 checksum ced3b2d1999f400f5d7aa04f536b72dc
Compiled from "ClassLoadMechanism.java"
2.2 类描述信息
public class com.yq.study.ClassLoadMechanism implements java.io.Serializable
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
2.3 常量池
Constant pool:
#1 = Methodref #10.#39 // java/lang/Object."":()V
#2 = Fieldref #9.#40 // com/yq/study/ClassLoadMechanism.mLoading:I
#3 = Fieldref #9.#41 // com/yq/study/ClassLoadMechanism.mRef:Ljava/lang/String;
#4 = Fieldref #42.#43 // java/lang/System.out:Ljava/io/PrintStream;
#5 = String #44 // instance fun be called!
#6 = Methodref #45.#46 // java/io/PrintStream.println:(Ljava/lang/String;)V
#7 = String #47 // static fun be called!
#8 = Fieldref #9.#48 // com/yq/study/ClassLoadMechanism.STATE_NON_FINAL:I
#9 = Class #49 // com/yq/study/ClassLoadMechanism
#10 = Class #50 // java/lang/Object
#11 = Class #51 // java/io/Serializable
#12 = Utf8 STATE_NON_FINAL
#13 = Utf8 I
#14 = Utf8 STATIC_BASIC
#15 = Utf8 ConstantValue
#16 = Integer 10
#17 = Utf8 STATIC_STR
#18 = Utf8 Ljava/lang/String;
#19 = String #52 // 123
#20 = Utf8 mLoading
#21 = Utf8 mRef
#22 = Utf8
#23 = Utf8 (ILjava/lang/String;)V
#24 = Utf8 Code
#25 = Utf8 LineNumberTable
#26 = Utf8 LocalVariableTable
#27 = Utf8 this
#28 = Utf8 Lcom/yq/study/ClassLoadMechanism;
#29 = Utf8 loading
#30 = Utf8 ref
#31 = Utf8 test
#32 = Utf8 ()V
#33 = Utf8 staticFun
#34 = Utf8 (I)V
#35 = Utf8 arg
#36 = Utf8
#37 = Utf8 SourceFile
#38 = Utf8 ClassLoadMechanism.java
#39 = NameAndType #22:#32 // "":()V
#40 = NameAndType #20:#13 // mLoading:I
#41 = NameAndType #21:#18 // mRef:Ljava/lang/String;
#42 = Class #53 // java/lang/System
#43 = NameAndType #54:#55 // out:Ljava/io/PrintStream;
#44 = Utf8 instance fun be called!
#45 = Class #56 // java/io/PrintStream
#46 = NameAndType #57:#58 // println:(Ljava/lang/String;)V
#47 = Utf8 static fun be called!
#48 = NameAndType #12:#13 // STATE_NON_FINAL:I
#49 = Utf8 com/yq/study/ClassLoadMechanism
#50 = Utf8 java/lang/Object
#51 = Utf8 java/io/Serializable
#52 = Utf8 123
#53 = Utf8 java/lang/System
#54 = Utf8 out
#55 = Utf8 Ljava/io/PrintStream;
#56 = Utf8 java/io/PrintStream
#57 = Utf8 println
#58 = Utf8 (Ljava/lang/String;)V
2.3 字段
private static int STATE_NON_FINAL;
descriptor: I
flags: ACC_PRIVATE, ACC_STATIC
private static final int STATIC_BASIC = 10;
descriptor: I
flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
ConstantValue: int 10
private static final java.lang.String STATIC_STR = "123";
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
ConstantValue: String 123
private final int mLoading;
descriptor: I
flags: ACC_PRIVATE, ACC_FINAL
private final java.lang.String mRef;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE, ACC_FINAL
2.4 方法
com.yq.study.ClassLoadMechanism(int, java.lang.String);
descriptor: (ILjava/lang/String;)V
flags:
Code:
stack=2, locals=3, args_size=3
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: aload_0
5: iload_1
6: putfield #2 // Field mLoading:I
9: aload_0
10: aload_2
11: putfield #3 // Field mRef:Ljava/lang/String;
14: return
LineNumberTable:
line 16: 0
line 17: 4
line 18: 9
line 19: 14
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lcom/yq/study/ClassLoadMechanism;
0 15 1 loading I
0 15 2 ref Ljava/lang/String;
public void test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5 // String instance fun be called!
5: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 22: 0
line 23: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/yq/study/ClassLoadMechanism;
public static void staticFun(int);
descriptor: (I)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #7 // String static fun be called!
5: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 26: 0
line 27: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 arg I
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 10
2: putstatic #8 // Field STATE_NON_FINAL:I
5: return
LineNumberTable:
line 9: 0
3. 虚拟机加载Class字节码面向对象设计
3.1 类封装
// C++ mirror of java.lang.Class
class MANAGED Class final : public Object {
private:
// Defining class loader, or null for the "bootstrap" system loader.
HeapReference class_loader_;
// DexCache of resolved constant pool entries
HeapReference dex_cache_;
// The interface table (iftable_) contains pairs of a interface class and an array of the
// interface methods.
HeapReference iftable_;
// Descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName
HeapReference name_;
// The superclass, or null if this is java.lang.Object or a primitive type.
HeapReference super_class_;
// Virtual method table (vtable)
HeapReference vtable_;
// instance fields
// ArtFields are allocated as a length prefixed ArtField array, and not an array of pointers to
// ArtFields.
uint64_t ifields_;
// Pointer to an ArtMethod length-prefixed array.
// where they are logically defined. This includes all private, static, final and virtual methods
uint64_t methods_;
// Static fields length-prefixed array.
uint64_t sfields_;
// Access flags; low 16 bits are defined by VM spec.
uint32_t access_flags_;
// Class flags to help speed up visiting object references.
uint32_t class_flags_;
// Total size of the Class instance; used when allocating storage on gc heap.
// See also object_size_.
uint32_t class_size_;
// Tid used to check for recursive invocation.
pid_t clinit_thread_id_;
static_assert(sizeof(pid_t) == sizeof(int32_t), "java.lang.Class.clinitThreadId size check");
// ClassDef index in dex file, -1 if no class definition such as an array.
// TODO: really 16bits
int32_t dex_class_def_idx_;
// Type index in dex file.
// TODO: really 16bits
int32_t dex_type_idx_;
// Number of instance fields that are object refs.
uint32_t num_reference_instance_fields_;
// Number of static fields that are object refs,
uint32_t num_reference_static_fields_;
// Total object size; used when allocating storage on gc heap.
// (For interfaces and abstract classes this will be zero.)
// See also class_size_.
uint32_t object_size_;
// Aligned object size for allocation fast path. The value is max uint32_t if the object is
// uninitialized or finalizable. Not currently used for variable sized objects.
uint32_t object_size_alloc_fast_path_;
// The lower 16 bits contains a Primitive::Type value. The upper 16
// bits contains the size shift of the primitive type.
uint32_t primitive_type_;
// Bitmap of offsets of ifields.
uint32_t reference_instance_offsets_;
// See the real definition in subtype_check_bits_and_status.h
// typeof(status_) is actually SubtypeCheckBitsAndStatus.
uint32_t status_;
// The offset of the first virtual method that is copied from an interface. This includes miranda,
// default, and default-conflict methods. Having a hard limit of ((2 << 16) - 1) for methods
// defined on a single class is well established in Java so we will use only uint16_t's here.
uint16_t copied_methods_offset_;
// The offset of the first declared virtual methods in the methods_ array.
uint16_t virtual_methods_offset_;
};
3.2 字段的封装
class ArtField final {
private:
GcRoot declaring_class_;
uint32_t access_flags_ = 0;
// Dex cache index of field id
uint32_t field_dex_idx_ = 0;
// Offset of field within an instance or in the Class' static fields
uint32_t offset_ = 0;
};
3.3 方法的封装
class ArtMethod final {
protected:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
GcRoot declaring_class_;
// Access flags; low 16 bits are defined by spec.
// Getting and setting this flag needs to be atomic when concurrency is
// possible, e.g. after this method's class is linked. Such as when setting
// verifier flags and single-implementation flag.
std::atomic access_flags_;
/* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */
// Offset to the CodeItem.
uint32_t dex_code_item_offset_;
// Index into method_ids of the dex file associated with this method.
uint32_t dex_method_index_;
/* End of dex file fields. */
// Entry within a dispatch table for this method. For static/direct methods the index is into
// the declaringClass.directMethods, for virtual methods the vtable and for interface methods the
// ifTable.
uint16_t method_index_;
union {
// Non-abstract methods: The hotness we measure for this method. Not atomic,
// as we allow missing increments: if the method is hot, we will see it eventually.
uint16_t hotness_count_;
// Abstract methods: IMT index (bitwise negated) or zero if it was not cached.
// The negation is needed to distinguish zero index and missing cached entry.
uint16_t imt_index_;
};
// Fake padding field gets inserted here.
// 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_;
4. 虚拟机加载字节码
4.1 从Classpath里加载系统类
// Finds the class in the boot class loader.
// If the class is found the method returns the resolved class. Otherwise it returns null.
ObjPtr ClassLinker::FindClassInBootClassLoaderClassPath(Thread* self,
const char* descriptor,
size_t hash) {
ObjPtr result = nullptr;
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
ObjPtr klass = LookupClass(self, descriptor, hash, nullptr);
if (klass != nullptr) {
result = EnsureResolved(self, descriptor, klass);
} else {
result = DefineClass(self,
descriptor,
hash,
ScopedNullHandle(),
*pair.first,
*pair.second);
}
if (result == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
FilterDexFileCaughtExceptions(self, this);
}
}
return result;
}
4.2 从Dex里加载
ObjPtr ClassLinker::FindClassInBaseDexClassLoaderClassPath(
ScopedObjectAccessAlreadyRunnable& soa,
const char* descriptor,
size_t hash,
Handle class_loader) {
DCHECK(IsPathOrDexClassLoader(soa, class_loader) ||
IsInMemoryDexClassLoader(soa, class_loader) ||
IsDelegateLastClassLoader(soa, class_loader))
<< "Unexpected class loader for descriptor " << descriptor;
ObjPtr ret;
auto define_class = [&](const DexFile* cp_dex_file) REQUIRES_SHARED(Locks::mutator_lock_) {
const dex::ClassDef* dex_class_def = OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
if (dex_class_def != nullptr) {
ObjPtr klass = DefineClass(soa.Self(),
descriptor,
hash,
class_loader,
*cp_dex_file,
*dex_class_def);
if (klass == nullptr) {
CHECK(soa.Self()->IsExceptionPending()) << descriptor;
FilterDexFileCaughtExceptions(soa.Self(), this);
// TODO: Is it really right to break here, and not check the other dex files?
} else {
DCHECK(!soa.Self()->IsExceptionPending());
}
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;
}
4.3 DefineClass方法
- SetupClass
- LoadClass
- LoadSuperAndInterfaces
- LinkClass
ObjPtr ClassLinker::DefineClass(Thread* self,
const char* descriptor,
size_t hash,
Handle class_loader,
const DexFile& dex_file,
const dex::ClassDef& dex_class_def) {
ScopedDefiningClass sdc(self);
StackHandleScope<3> hs(self);
auto klass = hs.NewHandle(nullptr);
// Load the class from the dex file.
if (UNLIKELY(!init_done_)) {
// finish up init of hand crafted class_roots_
if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
klass.Assign(GetClassRoot(this));
} else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
klass.Assign(GetClassRoot(this));
} else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
klass.Assign(GetClassRoot(this));
} else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) {
klass.Assign(GetClassRoot(this));
} else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {
klass.Assign(GetClassRoot(this));
} else if (strcmp(descriptor, "Ldalvik/system/ClassExt;") == 0) {
klass.Assign(GetClassRoot(this));
}
}
// For AOT-compilation of an app, we may use a shortened boot class path that excludes
// some runtime modules. Prevent definition of classes in app class loader that could clash
// with these modules as these classes could be resolved differently during execution.
if (class_loader != nullptr &&
Runtime::Current()->IsAotCompiler() &&
IsUpdatableBootClassPathDescriptor(descriptor)) {
ObjPtr pre_allocated =
Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
return sdc.Finish(nullptr);
}
// This is to prevent the calls to ClassLoad and ClassPrepare which can cause java/user-supplied
// code to be executed. We put it up here so we can avoid all the allocations associated with
// creating the class. This can happen with (eg) jit threads.
if (!self->CanLoadClasses()) {
// Make sure we don't try to load anything, potentially causing an infinite loop.
ObjPtr pre_allocated =
Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
return sdc.Finish(nullptr);
}
if (klass == 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.
if (CanAllocClass()) {
klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
} else {
return sdc.Finish(nullptr);
}
}
if (UNLIKELY(klass == nullptr)) {
self->AssertPendingOOMException();
return sdc.Finish(nullptr);
}
// Get the real dex file. This will return the input if there aren't any callbacks or they do
// nothing.
DexFile const* new_dex_file = nullptr;
dex::ClassDef const* new_class_def = nullptr;
// TODO We should ideally figure out some way to move this after we get a lock on the klass so it
// will only be called once.
Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor,
klass,
class_loader,
dex_file,
dex_class_def,
&new_dex_file,
&new_class_def);
// Check to see if an exception happened during runtime callbacks. Return if so.
if (self->IsExceptionPending()) {
return sdc.Finish(nullptr);
}
ObjPtr dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get());
if (dex_cache == nullptr) {
self->AssertPendingException();
return sdc.Finish(nullptr);
}
// Note: 设置class.dex_cache属性(Resolve后ArtField/ArtMethod会保存到这里)
klass->SetDexCache(dex_cache);
// Note: SetupClass
SetupClass(*new_dex_file, *new_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());
// Make sure we have a valid empty iftable even if there are errors.
klass->SetIfTable(GetClassRoot(this)->GetIfTable());
// Add the newly loaded class to the loaded classes table.
ObjPtr existing = InsertClass(descriptor, klass.Get(), hash);
if (existing != nullptr) {
// Note: We failed to insert because we raced with another thread. Calling EnsureResolved may cause
// this thread to block.
return sdc.Finish(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.
// Note: LoadClass
LoadClass(self, *new_dex_file, *new_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, ClassStatus::kErrorUnresolved, self);
}
return sdc.Finish(nullptr);
}
// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
// Note: LoadSuperAndInterfaces
if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self);
}
return sdc.Finish(nullptr);
}
CHECK(klass->IsLoaded());
// At this point the class is loaded. Publish a ClassLoad event.
// Note: this may be a temporary class. It is a listener's responsibility to handle this.
Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(klass);
// Link the class (if necessary)
CHECK(!klass->IsResolved());
// TODO: Use fast jobjects?
auto interfaces = hs.NewHandle>(nullptr);
MutableHandle h_new_class = hs.NewHandle(nullptr);
// Note: LinkClass
if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
// Linking failed.
if (!klass->IsErroneous()) {
mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self);
}
return sdc.Finish(nullptr);
}
self->AssertNoPendingException();
CHECK(h_new_class != nullptr) << descriptor;
CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor;
// Instrumentation may have updated entrypoints for all methods of all
// classes. However it could not update methods of this class while we
// were loading it. Now the class is resolved, we can update entrypoints
// as required by instrumentation.
if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
// We must be in the kRunnable state to prevent instrumentation from
// suspending all threads to update entrypoints while we are doing it
// for this class.
DCHECK_EQ(self->GetState(), kRunnable);
Runtime::Current()->GetInstrumentation()->InstallStubsForClass(h_new_class.Get());
}
/*
* We send CLASS_PREPARE events to the debugger from here. The
* definition of "preparation" is creating the static fields for a
* class and initializing them to the standard default values, but not
* executing any code (that comes later, during "initialization").
*
* We did the static preparation in LinkClass.
*
* The class has been prepared and resolved but possibly not yet verified
* at this point.
*/
Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(klass, h_new_class);
// Notify native debugger of the new class and its layout.
jit::Jit::NewTypeLoadedIfUsingJit(h_new_class.Get());
return sdc.Finish(h_new_class);
}
4.3.1 LoadClass
- 访问者模式读取Class字节码
- ArtField/ArtMethod分配内存并与Class的field/method关联
- LinkCode
void ClassLinker::LoadClass(Thread* self,
const DexFile& dex_file,
const dex::ClassDef& dex_class_def,
Handle klass) {
ClassAccessor accessor(dex_file,
dex_class_def,
/* parse_hiddenapi_class_data= */ klass->IsBootStrapClassLoaded());
if (!accessor.HasClassData()) {
return;
}
Runtime* const runtime = Runtime::Current();
{
// 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(__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());
LengthPrefixedArray* sfields = AllocArtFieldArray(self,
allocator,
accessor.NumStaticFields());
LengthPrefixedArray* ifields = AllocArtFieldArray(self,
allocator,
accessor.NumInstanceFields());
size_t num_sfields = 0u;
size_t num_ifields = 0u;
uint32_t last_static_field_idx = 0u;
uint32_t last_instance_field_idx = 0u;
// Methods
bool has_oat_class = false;
const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler())
? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class)
: OatFile::OatClass::Invalid();
const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr;
klass->SetMethodsPtr(
AllocArtMethodArray(self, allocator, accessor.NumMethods()),
accessor.NumDirectMethods(),
accessor.NumVirtualMethods());
size_t class_def_method_index = 0;
uint32_t last_dex_method_index = dex::kDexNoIndex;
size_t last_class_def_method_index = 0;
// Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the
// methods needs to decode all of the fields.
// Note: 访问类的所有的字段跟方法
accessor.VisitFieldsAndMethods([&](
const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) {
uint32_t field_idx = field.GetIndex();
DCHECK_GE(field_idx, last_static_field_idx); // Ordering enforced by DexFileVerifier.
if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) {
// Note: 加载静态字段
LoadField(field, klass, &sfields->At(num_sfields));
++num_sfields;
last_static_field_idx = field_idx;
}
}, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) {
uint32_t field_idx = field.GetIndex();
DCHECK_GE(field_idx, last_instance_field_idx); // Ordering enforced by DexFileVerifier.
if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) {
// Note: 加载实例字段
LoadField(field, klass, &ifields->At(num_ifields));
++num_ifields;
last_instance_field_idx = field_idx;
}
}, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index,
image_pointer_size_);
LoadMethod(dex_file, method, klass, art_method);
LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
uint32_t it_method_index = method.GetIndex();
if (last_dex_method_index == it_method_index) {
// duplicate case
art_method->SetMethodIndex(last_class_def_method_index);
} else {
art_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;
}, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* art_method = klass->GetVirtualMethodUnchecked(
class_def_method_index - accessor.NumDirectMethods(),
image_pointer_size_);
LoadMethod(dex_file, method, klass, art_method);
LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
++class_def_method_index;
});
if (UNLIKELY(num_ifields + num_sfields != accessor.NumFields())) {
LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor()
<< " (unique static fields: " << num_sfields << "/" << accessor.NumStaticFields()
<< ", unique instance fields: " << num_ifields << "/" << accessor.NumInstanceFields()
<< ")";
// NOTE: Not shrinking the over-allocated sfields/ifields, just setting size.
if (sfields != nullptr) {
sfields->SetSize(num_sfields);
}
if (ifields != nullptr) {
ifields->SetSize(num_ifields);
}
}
// Set the field arrays.
klass->SetSFieldsPtr(sfields);
DCHECK_EQ(klass->NumStaticFields(), num_sfields);
klass->SetIFieldsPtr(ifields);
DCHECK_EQ(klass->NumInstanceFields(), num_ifields);
}
// Ensure that the card is marked so that remembered sets pick up native roots.
WriteBarrier::ForEveryFieldWrite(klass.Get());
self->AllowThreadSuspension();
}
4.3.1.1 LoadField
void ClassLinker::LoadField(const ClassAccessor::Field& field,
Handle klass,
ArtField* dst) {
const uint32_t field_idx = field.GetIndex();
dst->SetDexFieldIndex(field_idx);
dst->SetDeclaringClass(klass.Get());
// Get access flags from the DexFile and set hiddenapi runtime access flags.
dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field));
}
4.3.1.2 LoadMethod
- 设置方法所属的类
- 设置方法的访问权限、符号引用(method_idx)、
- 如果是finalize方法则给class设置SetFinalizable()
void ClassLinker::LoadMethod(const DexFile& dex_file,
const ClassAccessor::Method& method,
Handle klass,
ArtMethod* dst) {
const uint32_t dex_method_idx = method.GetIndex();
const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);
ScopedAssertNoThreadSuspension ants("LoadMethod");
dst->SetDexMethodIndex(dex_method_idx);
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(method.GetCodeItemOffset());
// Get access flags from the DexFile and set hiddenapi runtime access flags.
uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method);
if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
// Set finalizable flag on declaring class.
if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) {
// Void return type.
if (klass->GetClassLoader() != nullptr) { // All non-boot finalizer methods are flagged.
klass->SetFinalizable();
} else {
std::string temp;
const char* klass_descriptor = klass->GetDescriptor(&temp);
// The Enum class declares a "final" finalize() method to prevent subclasses from
// introducing a finalizer. We don't want to set the finalizable flag for Enum or its
// subclasses, so we exclude it here.
// We also want to avoid setting the flag on Object, where we know that finalize() is
// empty.
if (strcmp(klass_descriptor, "Ljava/lang/Object;") != 0 &&
strcmp(klass_descriptor, "Ljava/lang/Enum;") != 0) {
klass->SetFinalizable();
}
}
}
} else if (method_name[0] == '<') {
// Fix broken access flags for initializers. Bug 11157540.
bool is_init = (strcmp("", method_name) == 0);
bool is_clinit = !is_init && (strcmp("", method_name) == 0);
if (UNLIKELY(!is_init && !is_clinit)) {
LOG(WARNING) << "Unexpected '<' at start of method name " << method_name;
} else {
if (UNLIKELY((access_flags & kAccConstructor) == 0)) {
LOG(WARNING) << method_name << " didn't have expected constructor access flag in class "
<< klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation();
access_flags |= kAccConstructor;
}
}
}
if (UNLIKELY((access_flags & kAccNative) != 0u)) {
// Check if the native method is annotated with @FastNative or @CriticalNative.
access_flags |= annotations::GetNativeMethodAnnotationAccessFlags(
dex_file, dst->GetClassDef(), dex_method_idx);
}
dst->SetAccessFlags(access_flags);
// Must be done after SetAccessFlags since IsAbstract depends on it.
if (klass->IsInterface() && dst->IsAbstract()) {
dst->CalculateAndSetImtIndex();
}
}
4.3.1.3 LinkCode
- ArtMethod设置quick_code(函数的入口地址)
static void LinkCode(ClassLinker* class_linker,
ArtMethod* method,
const OatFile::OatClass* oat_class,
uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension sants(__FUNCTION__);
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);
if (!method->IsInvokable()) {
EnsureThrowsInvocationError(class_linker, method);
return;
}
const void* quick_code = nullptr;
if (oat_class != nullptr) {
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);
quick_code = oat_method.GetQuickCode();
}
bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code);
// Note: this mimics the logic in image_writer.cc that installs the resolution
// stub only if we have compiled code and the method needs a class initialization
// check.
if (quick_code == nullptr) {
method->SetEntryPointFromQuickCompiledCode(
method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge());
} else if (enter_interpreter) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
} else if (NeedsClinitCheckBeforeCall(method)) {
DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized()); // Actually ClassStatus::Idx.
// If we do have code but the method needs a class initialization check before calling
// that code, install the resolution stub that will perform the check.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
} else {
method->SetEntryPointFromQuickCompiledCode(quick_code);
}
if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative();
if (enter_interpreter || quick_code == nullptr) {
// We have a native method here without code. Then it should have the generic JNI
// trampoline as entrypoint.
// TODO: this doesn't handle all the cases where trampolines may be installed.
DCHECK(class_linker->IsQuickGenericJniStub(method->GetEntryPointFromQuickCompiledCode()));
}
}
}
4.3.2 LinkClass
- LinkSuperClass
- LinkMethods
- LinkInstanceFields
- LinkStaticFields
bool ClassLinker::LinkClass(Thread* self,
const char* descriptor,
Handle klass,
Handle> interfaces,
MutableHandle* h_new_class_out) {
CHECK_EQ(ClassStatus::kLoaded, klass->GetStatus());
if (!LinkSuperClass(klass)) {
return false;
}
ArtMethod* imt_data[ImTable::kSize];
// If there are any new conflicts compared to super class.
bool new_conflict = false;
std::fill_n(imt_data, arraysize(imt_data), Runtime::Current()->GetImtUnimplementedMethod());
if (!LinkMethods(self, klass, interfaces, &new_conflict, imt_data)) {
return false;
}
if (!LinkInstanceFields(self, klass)) {
return false;
}
size_t class_size;
if (!LinkStaticFields(self, klass, &class_size)) {
return false;
}
CreateReferenceInstanceOffsets(klass);
CHECK_EQ(ClassStatus::kLoaded, klass->GetStatus());
ImTable* imt = nullptr;
if (klass->ShouldHaveImt()) {
// If there are any new conflicts compared to the super class we can not make a copy. There
// can be cases where both will have a conflict method at the same slot without having the same
// set of conflicts. In this case, we can not share the IMT since the conflict table slow path
// will possibly create a table that is incorrect for either of the classes.
// Same IMT with new_conflict does not happen very often.
if (!new_conflict) {
ImTable* super_imt = FindSuperImt(klass.Get(), image_pointer_size_);
if (super_imt != nullptr) {
bool imt_equals = true;
for (size_t i = 0; i < ImTable::kSize && imt_equals; ++i) {
imt_equals = imt_equals && (super_imt->Get(i, image_pointer_size_) == imt_data[i]);
}
if (imt_equals) {
imt = super_imt;
}
}
}
if (imt == nullptr) {
LinearAlloc* allocator = GetAllocatorForClassLoader(klass->GetClassLoader());
imt = reinterpret_cast(
allocator->Alloc(self, ImTable::SizeInBytes(image_pointer_size_)));
if (imt == nullptr) {
return false;
}
imt->Populate(imt_data, image_pointer_size_);
}
}
if (!klass->IsTemp() || (!init_done_ && klass->GetClassSize() == class_size)) {
// We don't need to retire this class as it has no embedded tables or it was created the
// correct size during class linker initialization.
CHECK_EQ(klass->GetClassSize(), class_size) << klass->PrettyDescriptor();
if (klass->ShouldHaveEmbeddedVTable()) {
klass->PopulateEmbeddedVTable(image_pointer_size_);
}
if (klass->ShouldHaveImt()) {
klass->SetImt(imt, image_pointer_size_);
}
// Update CHA info based on whether we override methods.
// Have to do this before setting the class as resolved which allows
// instantiation of klass.
if (LIKELY(descriptor != nullptr) && cha_ != nullptr) {
cha_->UpdateAfterLoadingOf(klass);
}
// This will notify waiters on klass that saw the not yet resolved
// class in the class_table_ during EnsureResolved.
mirror::Class::SetStatus(klass, ClassStatus::kResolved, self);
h_new_class_out->Assign(klass.Get());
} else {
CHECK(!klass->IsResolved());
// Retire the temporary class and create the correctly sized resolved class.
StackHandleScope<1> hs(self);
Handle h_new_class =
hs.NewHandle(mirror::Class::CopyOf(klass, self, class_size, imt, image_pointer_size_));
// Set arrays to null since we don't want to have multiple classes with the same ArtField or
// ArtMethod array pointers. If this occurs, it causes bugs in remembered sets since the GC
// may not see any references to the target space and clean the card for a class if another
// class had the same array pointer.
klass->SetMethodsPtrUnchecked(nullptr, 0, 0);
klass->SetSFieldsPtrUnchecked(nullptr);
klass->SetIFieldsPtrUnchecked(nullptr);
if (UNLIKELY(h_new_class == nullptr)) {
self->AssertPendingOOMException();
mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self);
return false;
}
CHECK_EQ(h_new_class->GetClassSize(), class_size);
ObjectLock lock(self, h_new_class);
FixupTemporaryDeclaringClass(klass.Get(), h_new_class.Get());
if (LIKELY(descriptor != nullptr)) {
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
const ObjPtr class_loader = h_new_class.Get()->GetClassLoader();
ClassTable* const table = InsertClassTableForClassLoader(class_loader);
const ObjPtr existing =
table->UpdateClass(descriptor, h_new_class.Get(), ComputeModifiedUtf8Hash(descriptor));
if (class_loader != nullptr) {
// We updated the class in the class table, perform the write barrier so that the GC knows
// about the change.
WriteBarrier::ForEveryFieldWrite(class_loader);
}
CHECK_EQ(existing, klass.Get());
if (log_new_roots_) {
new_class_roots_.push_back(GcRoot(h_new_class.Get()));
}
}
// Update CHA info based on whether we override methods.
// Have to do this before setting the class as resolved which allows
// instantiation of klass.
if (LIKELY(descriptor != nullptr) && cha_ != nullptr) {
cha_->UpdateAfterLoadingOf(h_new_class);
}
// This will notify waiters on temp class that saw the not yet resolved class in the
// class_table_ during EnsureResolved.
mirror::Class::SetStatus(klass, ClassStatus::kRetired, self);
CHECK_EQ(h_new_class->GetStatus(), ClassStatus::kResolving);
// This will notify waiters on new_class that saw the not yet resolved
// class in the class_table_ during EnsureResolved.
mirror::Class::SetStatus(h_new_class, ClassStatus::kResolved, self);
// Return the new class.
h_new_class_out->Assign(h_new_class.Get());
}
return true;
}
4.3.2.1 LinkSuperClass
- 父类语法校验(Object基类不可以有父类、父类访问权限、是否final等)
bool ClassLinker::LinkSuperClass(Handle klass) {
CHECK(!klass->IsPrimitive());
ObjPtr super = klass->GetSuperClass();
ObjPtr object_class = GetClassRoot(this);
if (klass.Get() == object_class) {
if (super != nullptr) {
ThrowClassFormatError(klass.Get(), "java.lang.Object must not have a superclass");
return false;
}
return true;
}
if (super == nullptr) {
ThrowLinkageError(klass.Get(), "No superclass defined for class %s",
klass->PrettyDescriptor().c_str());
return false;
}
// Verify
if (klass->IsInterface() && super != object_class) {
ThrowClassFormatError(klass.Get(), "Interfaces must have java.lang.Object as superclass");
return false;
}
if (super->IsFinal()) {
ThrowVerifyError(klass.Get(),
"Superclass %s of %s is declared final",
super->PrettyDescriptor().c_str(),
klass->PrettyDescriptor().c_str());
return false;
}
if (super->IsInterface()) {
ThrowIncompatibleClassChangeError(klass.Get(),
"Superclass %s of %s is an interface",
super->PrettyDescriptor().c_str(),
klass->PrettyDescriptor().c_str());
return false;
}
if (!klass->CanAccess(super)) {
ThrowIllegalAccessError(klass.Get(), "Superclass %s is inaccessible to class %s",
super->PrettyDescriptor().c_str(),
klass->PrettyDescriptor().c_str());
return false;
}
// Inherit kAccClassIsFinalizable from the superclass in case this
// class doesn't override finalize.
if (super->IsFinalizable()) {
klass->SetFinalizable();
}
// Inherit class loader flag form super class.
if (super->IsClassLoaderClass()) {
klass->SetClassLoaderClass();
}
// Inherit reference flags (if any) from the superclass.
uint32_t reference_flags = (super->GetClassFlags() & mirror::kClassFlagReference);
if (reference_flags != 0) {
CHECK_EQ(klass->GetClassFlags(), 0u);
klass->SetClassFlags(klass->GetClassFlags() | reference_flags);
}
// Disallow custom direct subclasses of java.lang.ref.Reference.
if (init_done_ && super == GetClassRoot(this)) {
ThrowLinkageError(klass.Get(),
"Class %s attempts to subclass java.lang.ref.Reference, which is not allowed",
klass->PrettyDescriptor().c_str());
return false;
}
if (kIsDebugBuild) {
// Ensure super classes are fully resolved prior to resolving fields..
while (super != nullptr) {
CHECK(super->IsResolved());
super = super->GetSuperClass();
}
}
return true;
}
4.3.2.2 LinkMethods
- SetupInterfaceLookupTable
- LinkVirtualMethods (设置vtable)
- LinkInterfaceMethods
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);
}
4.3.2.3 LinkField
- 计算Static/Instance字段的MemberOffset
bool ClassLinker::LinkFields(Thread* self,
Handle klass,
bool is_static,
size_t* class_size) {
self->AllowThreadSuspension();
const size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields();
LengthPrefixedArray* const fields = is_static ? klass->GetSFieldsPtr() :
klass->GetIFieldsPtr();
// Initialize field_offset
MemberOffset field_offset(0);
if (is_static) {
field_offset = klass->GetFirstReferenceStaticFieldOffsetDuringLinking(image_pointer_size_);
} else {
ObjPtr super_class = klass->GetSuperClass();
if (super_class != nullptr) {
CHECK(super_class->IsResolved())
<< klass->PrettyClass() << " " << super_class->PrettyClass();
field_offset = MemberOffset(super_class->GetObjectSize());
}
}
CHECK_EQ(num_fields == 0, fields == nullptr) << klass->PrettyClass();
// we want a relatively stable order so that adding new fields
// minimizes disruption of C++ version such as Class and Method.
//
// The overall sort order order is:
// 1) All object reference fields, sorted alphabetically.
// 2) All java long (64-bit) integer fields, sorted alphabetically.
// 3) All java double (64-bit) floating point fields, sorted alphabetically.
// 4) All java int (32-bit) integer fields, sorted alphabetically.
// 5) All java float (32-bit) floating point fields, sorted alphabetically.
// 6) All java char (16-bit) integer fields, sorted alphabetically.
// 7) All java short (16-bit) integer fields, sorted alphabetically.
// 8) All java boolean (8-bit) integer fields, sorted alphabetically.
// 9) All java byte (8-bit) integer fields, sorted alphabetically.
//
// Once the fields are sorted in this order we will attempt to fill any gaps that might be present
// in the memory layout of the structure. See ShuffleForward for how this is done.
std::deque grouped_and_sorted_fields;
const char* old_no_suspend_cause = self->StartAssertNoThreadSuspension(
"Naked ArtField references in deque");
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());
// References should be at the front.
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; // past last reference, move on to the next phase
}
if (UNLIKELY(!IsAligned)>(
field_offset.Uint32Value()))) {
MemberOffset old_offset = field_offset;
field_offset = MemberOffset(RoundUp(field_offset.Uint32Value(), 4));
AddFieldGap(old_offset.Uint32Value(), field_offset.Uint32Value(), &gaps);
}
DCHECK_ALIGNED(field_offset.Uint32Value(), sizeof(mirror::HeapReference));
grouped_and_sorted_fields.pop_front();
num_reference_fields++;
field->SetOffset(field_offset);
field_offset = MemberOffset(field_offset.Uint32Value() +
sizeof(mirror::HeapReference));
}
// Gaps are stored as a max heap which means that we must shuffle from largest to smallest
// otherwise we could end up with suboptimal gap fills.
ShuffleForward<8>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps);
ShuffleForward<4>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps);
ShuffleForward<2>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps);
ShuffleForward<1>(¤t_field, &field_offset, &grouped_and_sorted_fields, &gaps);
CHECK(grouped_and_sorted_fields.empty()) << "Missed " << grouped_and_sorted_fields.size() <<
" fields.";
self->EndAssertNoThreadSuspension(old_no_suspend_cause);
// We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it.
if (!is_static && klass->DescriptorEquals("Ljava/lang/ref/Reference;")) {
// We know there are no non-reference fields in the Reference classes, and we know
// that 'referent' is alphabetically last, so this is easy...
CHECK_EQ(num_reference_fields, num_fields) << klass->PrettyClass();
CHECK_STREQ(fields->At(num_fields - 1).GetName(), "referent")
<< klass->PrettyClass();
--num_reference_fields;
}
size_t size = field_offset.Uint32Value();
// Update klass
if (is_static) {
klass->SetNumReferenceStaticFields(num_reference_fields);
*class_size = size;
} else {
klass->SetNumReferenceInstanceFields(num_reference_fields);
ObjPtr super_class = klass->GetSuperClass();
if (num_reference_fields == 0 || super_class == nullptr) {
// object has one reference field, klass, but we ignore it since we always visit the class.
// super_class is null iff the class is java.lang.Object.
if (super_class == nullptr ||
(super_class->GetClassFlags() & mirror::kClassFlagNoReferenceFields) != 0) {
klass->SetClassFlags(klass->GetClassFlags() | mirror::kClassFlagNoReferenceFields);
}
}
if (kIsDebugBuild) {
DCHECK_EQ(super_class == nullptr, klass->DescriptorEquals("Ljava/lang/Object;"));
size_t total_reference_instance_fields = 0;
ObjPtr cur_super = klass.Get();
while (cur_super != nullptr) {
total_reference_instance_fields += cur_super->NumReferenceInstanceFieldsDuringLinking();
cur_super = cur_super->GetSuperClass();
}
if (super_class == nullptr) {
CHECK_EQ(total_reference_instance_fields, 1u) << klass->PrettyDescriptor();
} else {
// Check that there is at least num_reference_fields other than Object.class.
CHECK_GE(total_reference_instance_fields, 1u + num_reference_fields)
<< klass->PrettyClass();
}
}
if (!klass->IsVariableSize()) {
std::string temp;
DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp);
size_t previous_size = klass->GetObjectSize();
if (previous_size != 0) {
// Make sure that we didn't originally have an incorrect size.
CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp);
}
klass->SetObjectSize(size);
}
}
if (kIsDebugBuild) {
// Make sure that the fields array is ordered by name but all reference
// offsets are at the beginning as far as alignment allows.
MemberOffset start_ref_offset = is_static
? klass->GetFirstReferenceStaticFieldOffsetDuringLinking(image_pointer_size_)
: klass->GetFirstReferenceInstanceFieldOffset();
MemberOffset end_ref_offset(start_ref_offset.Uint32Value() +
num_reference_fields *
sizeof(mirror::HeapReference));
MemberOffset current_ref_offset = start_ref_offset;
for (size_t i = 0; i < num_fields; i++) {
ArtField* field = &fields->At(i);
VLOG(class_linker) << "LinkFields: " << (is_static ? "static" : "instance")
<< " class=" << klass->PrettyClass() << " field=" << field->PrettyField()
<< " offset=" << field->GetOffsetDuringLinking();
if (i != 0) {
ArtField* const prev_field = &fields->At(i - 1);
// NOTE: The field names can be the same. This is not possible in the Java language
// but it's valid Java/dex bytecode and for example proguard can generate such bytecode.
DCHECK_LE(strcmp(prev_field->GetName(), field->GetName()), 0);
}
Primitive::Type type = field->GetTypeAsPrimitiveType();
bool is_primitive = type != Primitive::kPrimNot;
if (klass->DescriptorEquals("Ljava/lang/ref/Reference;") &&
strcmp("referent", field->GetName()) == 0) {
is_primitive = true; // We lied above, so we have to expect a lie here.
}
MemberOffset offset = field->GetOffsetDuringLinking();
if (is_primitive) {
if (offset.Uint32Value() < end_ref_offset.Uint32Value()) {
// Shuffled before references.
size_t type_size = Primitive::ComponentSize(type);
CHECK_LT(type_size, sizeof(mirror::HeapReference));
CHECK_LT(offset.Uint32Value(), start_ref_offset.Uint32Value());
CHECK_LE(offset.Uint32Value() + type_size, start_ref_offset.Uint32Value());
CHECK(!IsAligned)>(offset.Uint32Value()));
}
} else {
CHECK_EQ(current_ref_offset.Uint32Value(), offset.Uint32Value());
current_ref_offset = MemberOffset(current_ref_offset.Uint32Value() +
sizeof(mirror::HeapReference));
}
}
CHECK_EQ(current_ref_offset.Uint32Value(), end_ref_offset.Uint32Value());
}
return true;
}
4 解析Resolve
- 符号引用转成直接引用的过程
- ResolveType(Class)
- ResolveArtField
- ResolveArtMethod
4.1 ResolveType
inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx,
ObjPtr referrer) {
if (kObjPtrPoisoning) {
StackHandleScope<1> hs(Thread::Current());
HandleWrapperObjPtr referrer_wrapper = hs.NewHandleWrapper(&referrer);
Thread::Current()->PoisonObjectPointers();
}
DCHECK(!Thread::Current()->IsExceptionPending());
// We do not need the read barrier for getting the DexCache for the initial resolved type
// lookup as both from-space and to-space copies point to the same native resolved types array.
ObjPtr resolved_type =
referrer->GetDexCache()->GetResolvedType(type_idx);
if (resolved_type == nullptr) {
resolved_type = DoResolveType(type_idx, referrer);
}
return resolved_type;
}
4.2 ResolveMethod
template
inline ArtMethod* ClassLinker::ResolveMethod(Thread* self,
uint32_t method_idx,
ArtMethod* referrer,
InvokeType type) {
DCHECK(referrer != nullptr);
// Note: The referrer can be a Proxy constructor. In that case, we need to do the
// lookup in the context of the original method from where it steals the code.
// However, we delay the GetInterfaceMethodIfProxy() until needed.
DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
Thread::PoisonObjectPointersIfDebug();
// We do not need the read barrier for getting the DexCache for the initial resolved method
// lookup as both from-space and to-space copies point to the same native resolved methods array.
ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod(
method_idx, image_pointer_size_);
DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod());
if (UNLIKELY(resolved_method == nullptr)) {
referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
ObjPtr declaring_class = referrer->GetDeclaringClass();
StackHandleScope<2> hs(self);
Handle h_dex_cache(hs.NewHandle(referrer->GetDexCache()));
Handle h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
resolved_method = ResolveMethod(method_idx,
h_dex_cache,
h_class_loader,
referrer,
type);
} else if (kResolveMode == ResolveMode::kCheckICCEAndIAE) {
referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
// Check if the invoke type matches the class type.
ObjPtr dex_cache = referrer->GetDexCache();
ObjPtr class_loader = referrer->GetClassLoader();
if (CheckInvokeClassMismatch(dex_cache, type, method_idx, class_loader)) {
DCHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
// Check access.
ObjPtr referring_class = referrer->GetDeclaringClass();
if (!referring_class->CheckResolvedMethodAccess(resolved_method->GetDeclaringClass(),
resolved_method,
dex_cache,
method_idx,
type)) {
DCHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
// Check if the invoke type matches the method type.
if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
ThrowIncompatibleClassChangeError(type,
resolved_method->GetInvokeType(),
resolved_method,
referrer);
return nullptr;
}
}
// Note: We cannot check here to see whether we added the method to the cache. It
// might be an erroneous class, which results in it being hidden from us.
return resolved_method;
}
4.3 ResolvedField
inline ArtField* ClassLinker::ResolveField(uint32_t field_idx,
ArtMethod* referrer,
bool is_static) {
Thread::PoisonObjectPointersIfDebug();
// We do not need the read barrier for getting the DexCache for the initial resolved field
// lookup as both from-space and to-space copies point to the same native resolved fields array.
ArtField* resolved_field = referrer->GetDexCache()->GetResolvedField(
field_idx, image_pointer_size_);
if (UNLIKELY(resolved_field == nullptr)) {
StackHandleScope<2> hs(Thread::Current());
ObjPtr referring_class = referrer->GetDeclaringClass();
Handle dex_cache(hs.NewHandle(referrer->GetDexCache()));
Handle class_loader(hs.NewHandle(referring_class->GetClassLoader()));
resolved_field = ResolveField(field_idx, dex_cache, class_loader, is_static);
// Note: We cannot check here to see whether we added the field to the cache. The type
// might be an erroneous class, which results in it being hidden from us.
}
return resolved_field;
}
5. 类初始化(ClassLinker::InitializeClass)
- (clinit)ArtMethod::Invoke
bool ClassLinker::InitializeClass(Thread* self,
Handle klass,
bool can_init_statics,
bool can_init_parents) {
// 去掉大部分的代码逻辑
if (!self->IsExceptionPending()) {
ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
if (clinit != nullptr) {
CHECK(can_init_statics);
JValue result;
clinit->Invoke(self, nullptr, 0, &result, "V");
}
}
...
return success;
}
5.1 ArtMethod::Invoke
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
ThrowStackOverflowError(self);
return;
}
if (kIsDebugBuild) {
self->AssertThreadSuspensionIsAllowable();
CHECK_EQ(kRunnable, self->GetState());
CHECK_STREQ(GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(), 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.
if (UNLIKELY(!runtime->IsStarted() ||
(self->IsForceInterpreter() && !IsNative() && !IsProxyMethod() && IsInvokable()))) {
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 {
DCHECK_EQ(runtime->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
constexpr bool kLogInvocationStartAndReturn = false;
bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
if (LIKELY(have_quick_code)) {
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf(
"Invoking '%s' quick code=%p static=%d", PrettyMethod().c_str(),
GetEntryPointFromQuickCompiledCode(), static_cast(IsStatic() ? 1 : 0));
}
// Ensure that we won't be accidentally calling quick compiled code when -Xint.
if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
CHECK(!runtime->UseJitCompilation());
const void* oat_quick_code =
(IsNative() || !IsInvokable() || IsProxyMethod() || IsObsolete())
? nullptr
: GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize());
CHECK(oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode())
<< "Don't call compiled code when -Xint " << PrettyMethod();
}
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);
}
}
// Pop transition.
self->PopManagedStackFragment(fragment);
}