android类加载源码分析
Dex文件加载过程#
PathClassLoader 和 DexClassLoader都可以加载dex文件,其对应的基类都是BaseDexClassLoader。在new一个PathClassLoader/DexClassLoader对象时就会调用其对应的构造函数,然后调用父类BaseDexClassLoader的构造函数。最终的调用链为:
BaseDexClassLoader()
├─ new DexPathList
│ └─ DexPathList.makeDexElements
│ └─ DexPathList.loadDexFile
│ └─ new DexFile
│ └─ DexFile.openDexFile
└─────────────────└─ DexFile.openDexFileNative
Method | file |
---|---|
BaseDexClassLoader() | libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java |
new DexPathList | libcore/dalvik/src/main/java/dalvik/system/DexPathList.java |
DexPathList.makeDexElements | ... |
DexPathList.loadDexFile | ... |
new DexFile | libcore/dalvik/src/main/java/dalvik/system/DexFile.java |
DexFile.openDexFile | ... |
DexFile.openDexFileNative | ... |
DexFile_openDexFileNative#
DexFile.openDexFileNative对应的native函数为DexFile_openDexFileNative,此函数调用的链路如下
DexFile_openDexFileNative()
├─ OpenDexFilesFromOat
│ └─ ArtDexFileLoader::Open
│ └─ OpenAndReadMagic
│ OpenWithMagic
│ └─ ArtDexFileLoader::OpenFile
│ └─ ArtDexFileLoader::OpenCommon
│ └─ DexFileLoader::OpenCommon
│ magic == "dex\n" new StandardDexFile
│ magic == "cdex" new CompactDexFile
└———————————————————————└─ DexFile::DexFile()
首先DexFile_openDexFileNative函数会调用OpenDexFilesFromOat
//art/runtime/native/dalvik_system_DexFile.cc
static jobject DexFile_openDexFileNative(JNIEnv* env,
jclass,
jstring javaSourceName,
jstring javaOutputName ATTRIBUTE_UNUSED,
jint flags ATTRIBUTE_UNUSED,
jobject class_loader,
jobjectArray dex_elements) {
std::vector<std::string> error_msgs;
const OatFile* oat_file = nullptr;
std::vector<std::unique_ptr<const DexFile>> dex_files =
Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
class_loader,
dex_elements,
/*out*/ &oat_file,
/*out*/ &error_msgs);
}
OatFileManager::OpenDexFilesFromOat#
OpenDexFilesFromOat会调用dex_file_loader.Open (ArtDexFileLoader::Open)去加载dex文件
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
const char* dex_location,
jobject class_loader,
jobjectArray dex_elements,
const OatFile** out_oat_file,
std::vector<std::string>* error_msgs) {
// Fall back to running out of the original dex file if we couldn't load any
// dex_files from the oat file.
if (dex_files.empty()) {
if (oat_file_assistant.HasOriginalDexFiles()) {
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
static constexpr bool kVerifyChecksum = true;
const ArtDexFileLoader dex_file_loader;
if (!dex_file_loader.Open(dex_location,
dex_location,
Runtime::Current()->IsVerificationEnabled(),
kVerifyChecksum,
/*out*/ &error_msg,
&dex_files)) {
LOG(WARNING) << error_msg;
error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
+ " because: " + error_msg);
}
} else {
error_msgs->push_back("Fallback mode disabled, skipping dex files.");
}
} else {
error_msgs->push_back("No original dex files found for dex location "
+ std::string(dex_location));
}
}
return dex_files;
}
ArtDexFileLoader::Open#
ArtDexFileLoader::Open会调用OpenWithMagic
bool ArtDexFileLoader::Open(const char* filename,
const std::string& location,
bool verify,
bool verify_checksum,
std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
uint32_t magic;
File fd = OpenAndReadMagic(filename, &magic, error_msg);
if (fd.Fd() == -1) {
DCHECK(!error_msg->empty());
return false;
}
return OpenWithMagic(
magic, fd.Release(), location, verify, verify_checksum, error_msg, dex_files);
}
ArtDexFileLoader::OpenWithMagic#
ArtDexFileLoader::OpenWithMagic会判断文件是zip(apk)文件还是dex文件,如果是zip文件就去调用OpenZip,反之调用OpenFile
bool ArtDexFileLoader::OpenWithMagic(uint32_t magic,
int fd,
const std::string& location,
bool verify,
bool verify_checksum,
std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
ScopedTrace trace(std::string("Open dex file ") + std::string(location));
DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
if (IsZipMagic(magic)) {
return OpenZip(fd, location, verify, verify_checksum, error_msg, dex_files);
}
if (IsMagicValid(magic)) {
std::unique_ptr<const DexFile> dex_file(OpenFile(fd,
location,
verify,
verify_checksum,
/* mmap_shared= */ false,
error_msg));
if (dex_file.get() != nullptr) {
dex_files->push_back(std::move(dex_file));
return true;
} else {
return false;
}
}
*error_msg = StringPrintf("Expected valid zip or dex file: '%s'", location.c_str());
return false;
}
ArtDexFileLoader::OpenFile#
ArtDexFileLoader::OpenFile主要代码如下,将dex文件从磁盘中map到内存中并调用ArtDexFileLoader::OpenCommon。接着rtDexFileLoader::OpenCommon还会去调用DexFileLoader::OpenCommon。
//art/libdexfile/dex/art_dex_file_loader.cc
std::unique_ptr<const DexFile> ArtDexFileLoader::OpenFile(int fd,
const std::string& location,
bool verify,
bool verify_checksum,
bool mmap_shared,
std::string* error_msg) const {
...
//将磁盘文件map到内存中
MemMap map;
map = MemMap::MapFile(length,
PROT_READ,
mmap_shared ? MAP_SHARED : MAP_PRIVATE,
fd,
0,
/*low_4gb=*/false,
location.c_str(),
error_msg);
std::unique_ptr<DexFile> dex_file = OpenCommon(begin,
size,
/*data_base=*/ nullptr,
/*data_size=*/ 0u,
location,
dex_header->checksum_,
kNoOatDexFile,
verify,
verify_checksum,
error_msg,
std::make_unique<MemMapContainer>(std::move(map)),
/*verify_result=*/ nullptr);
return dex_file;
}
DexFileLoader::OpenCommon#
DexFileLoader::OpenCommon会判断dex文件的magic是来判断文件是标准型的还是紧凑型的。
- 如果magic == “dex\n”则证明dex文件是标准型的,会去调用new StandardDexFile()
- 如果magic == “cdex” 则证明dex文件是紧凑型的,会去调用new CompactDexFile()
- StandardDexFile与CompactDexFile都继承与DexFile,所以都会去调用DexFile::DexFile()构造函数
//art/libdexfile/dex/dex_file_loader.cc
std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
size_t size,
const uint8_t* data_base,
size_t data_size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
bool verify_checksum,
std::string* error_msg,
std::unique_ptr<DexFileContainer> container,
VerifyResult* verify_result) {
if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) {
//如果Magic == “dex\n”
dex_file.reset(new StandardDexFile(base,
size,
location,
location_checksum,
oat_dex_file,
std::move(container)));
} else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
//Magic == “cdex”
dex_file.reset(new CompactDexFile(base,
size,
data_base,
data_size,
location,
location_checksum,
oat_dex_file,
std::move(container)));
// Disable verification for CompactDex input.
verify = false;
} else {
*error_msg = "Invalid or truncated dex file";
}
return dex_file;
}
android类加载过程#
ClassLoader::LoadClass#
ClassLoader类是所有类加载器的虚基类,在静态/动态加载一个类时会首先调用此虚基类的LoadClass函数。
//libcore/ojluni/src/main/java/java/lang/ClassLoader.java
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
//查找此类是否已经加载
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
//父加载器不为空就先让父加载器加载此类
c = parent.loadClass(name, false);
} else {
//父加载器为空说明此加载器为根加载器BootClassLoader,让根加载器去加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
//如果父加载器都加载不了就让当前类加载器调用findclass去加载。
c = findClass(name);
}
}
return c;
}
BaseDexClassLoader::findClass#
因为BaseDexClassLoader继承了ClassLoader虚类,其他加载器的类基本都继承于BaseDexClassLoader类,BaseDexClassLoader类中重写了findClass方法,所以上一步会去调用BaseDexClassLoader类中的findClass方法。
//libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
protected Class<?> findClass(String name) throws ClassNotFoundException {
...
// Check whether the class in question is present in the dexPath that
// this classloader operates on.
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
Class c = pathList.findClass(name, suppressedExceptions);
if (c != null) {
return c;
}
}
BaseDexClassLoader的findclass会继续调用DexPathList的findClass方法
//libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
public Class<?> findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
Class<?> clazz = element.findClass(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
DexPathList的findClass方法调用Element类的findClass方法。
//libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
public Class<?> findClass(String name, ClassLoader definingContext,
List<Throwable> suppressed) {
return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
: null;
}
defineClassNative#
Element类的findClass方法去调用loadClassBinaryName函数,loadClassBinaryName函数接着会调用defineClass->defineClassNative。defineClassNative函数是一个native函数在art虚拟机中实现。
//libcore/dalvik/src/main/java/dalvik/system/DexFile.java
public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
return defineClass(name, loader, mCookie, this, suppressed);
}
private static Class defineClass(String name, ClassLoader loader, Object cookie,
DexFile dexFile, List<Throwable> suppressed) {
Class result = null;
try {
result = defineClassNative(name, loader, cookie, dexFile);
} catch (NoClassDefFoundError e) {
if (suppressed != null) {
suppressed.add(e);
}
} catch (ClassNotFoundException e) {
if (suppressed != null) {
suppressed.add(e);
}
}
return result;
}
defineClassNative对应的art中的native函数为DexFile_defineClassNative,在art中进行类的字段,方法的加载。
DexFile_defineClassNative#
DexFile_defineClassNative函数调用链如下
DexFile_defineClassNative()
├─ FindClassDef
│ ClassLinker::DefineClass
│ └─ ClassLinker::LoadClass
│ └─ LoadField
│ LoadMethod
│ LinkCode
└─ InsertDexFileInToClassLoader
- FindClassDef枚举此类加载器的所有dex文件,并在这些dex文件中寻找Class_def看是否等于当前加载类,如果找不到则返回失败
- ClassLinker::DefineClass加载对应类的字段和方法
- 将对应的DexFile添加到类加载器对应的ClassTable中
//art/runtime/native/dalvik_system_DexFile.cc
static jclass DexFile_defineClassNative(JNIEnv* env,
jclass,
jstring javaName,
jobject javaLoader,
jobject cookie,
jobject dexFile) {
std::vector<const DexFile*> dex_files;
const std::string descriptor(DotToDescriptor(class_name.c_str()));
const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
//枚举此类加载器的所有dex文件
for (auto& dex_file : dex_files) {
//找到dex中的ClassDef
const dex::ClassDef* dex_class_def =
OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash);
if (dex_class_def != nullptr) {
ScopedObjectAccess soa(env);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
ObjPtr<mirror::DexCache> dex_cache =
class_linker->RegisterDexFile(*dex_file, class_loader.Get());
if (dex_cache == nullptr) {
// OOME or InternalError (dexFile already registered with a different class loader).
soa.Self()->AssertPendingException();
return nullptr;
}
//完成目标类的加载
ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),
descriptor.c_str(),
hash,
class_loader,
*dex_file,
*dex_class_def);
//将对应的DexFile添加到类加载器对应的ClassTable中
// Add the used dex file. This only required for the DexFile.loadClass API since normal
// class loaders already keep their dex files live.
class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),
class_loader.Get());
}
ClassLinker::DefineClass#
DefineClass主要就是调用ClassLinker::LoadClass加载对应类的字段和方法
//art/runtime/class_linker.cc
ObjPtr<mirror::Class> ClassLinker::DefineClass(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const dex::ClassDef& dex_class_def) {
//从DexFile dex文件中(内存中的)加载所有的field和method
// 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, *new_dex_file, *new_class_def, klass);
}
ClassLinker::LoadClass#
ClassLinker::LoadClass调用LoadField加载所有的字段,调用LoadMethod加载所有的方法。
//art/runtime/class_linker.cc
void ClassLinker::LoadClass(Thread* self,
const DexFile& dex_file,
const dex::ClassDef& dex_class_def,
Handle<mirror::Class> klass) {
uint16_t hotness_threshold = runtime->GetJITOptions()->GetWarmupThreshold();
// Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the
// methods needs to decode all of the fields.
accessor.VisitFieldsAndMethods([&](
//加载所有的静态field
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)) {
LoadField(field, klass, &sfields->At(num_sfields));
++num_sfields;
last_static_field_idx = field_idx;
}
}, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) {
//加载所有的field
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)) {
LoadField(field, klass, &ifields->At(num_ifields));
++num_ifields;
last_instance_field_idx = field_idx;
}
}, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
//加载所有的对象方法(direct method)
ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index,
image_pointer_size_);
LoadMethod(dex_file, method, klass.Get(), 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;
}
art_method->ResetCounter(hotness_threshold);
++class_def_method_index;
}, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
//加载所有的抽象方法(virtual method)
ArtMethod* art_method = klass->GetVirtualMethodUnchecked(
class_def_method_index - accessor.NumDirectMethods(),
image_pointer_size_);
art_method->ResetCounter(hotness_threshold);
LoadMethod(dex_file, method, klass.Get(), art_method);
LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
++class_def_method_index;
});
}
参考链接:https://evilpan.com/2021/12/26/art-internal/#文件加载
源码基于android 10
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!