代码参考Android 8.0。
一、Dex加载流程
dex加载简要时序图DexPathList的构造方法中执行makeDexElements,最终产出的数据结构为Element[],而Element作为元素,其数据结构如下:
Element[]数据结构整个流程中,native获取DexFile集的核心函数为:OatFileManager::OpenDexFilesFromOat,该方法核心逻辑如下:
art/runtime/oat_file_manager.cc
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
const char* dex_location,//加载的dex文件路径
jobject class_loader,//外部传入的classLoader
jobjectArray dex_elements,//外部传入的Elements[]
const OatFile** out_oat_file,//OatFile对象
std::vector<std::string>* error_msgs) {
ScopedTrace trace(__FUNCTION__);
CHECK(dex_location != nullptr);
CHECK(error_msgs != nullptr);
// Verify we aren't holding the mutator lock, which could starve GC if we
// have to generate or relocate an oat file.
Thread* const self = Thread::Current();
Locks::mutator_lock_->AssertNotHeld(self);
Runtime* const runtime = Runtime::Current();
//oatfile处理类
OatFileAssistant oat_file_assistant(dex_location,
kRuntimeISA,
!runtime->IsAotCompiler());
// Lock the target oat location to avoid races generating and loading the
// oat file.
std::string error_msg;
if (!oat_file_assistant.Lock(/*out*/&error_msg)) {
// Don't worry too much if this fails. If it does fail, it's unlikely we
// can generate an oat file anyway.
VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
}
const OatFile* source_oat_file = nullptr;
//判断是否走dex2oat编译,如下情况满足则会执行编译:
//1.有dex文件,但还没编过
//2\. 编译后的odex过期了,比如:系统升级导致oat文件不能匹配boot image,或者oat文件不能匹配compiler filter等。
if (!oat_file_assistant.IsUpToDate()) {
// Update the oat file on disk if we can, based on the --compiler-filter
// option derived from the current runtime options.
// This may fail, but that's okay. Best effort is all that matters here.
//执行dex2oat编译
switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)) {
case OatFileAssistant::kUpdateFailed:
LOG(WARNING) << error_msg;
break;
case OatFileAssistant::kUpdateNotAttempted:
// Avoid spamming the logs if we decided not to attempt making the oat
// file up to date.
VLOG(oat) << error_msg;
break;
case OatFileAssistant::kUpdateSucceeded:
// Nothing to do.
break;
}
}
// Get the oat file on disk.
//获取当前有效的.odex
std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
//odex冲突检测
if (oat_file != nullptr) {
// Take the file only if it has no collisions, or we must take it because of preopting.
bool accept_oat_file =
!HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
if (!accept_oat_file) {
// Failed the collision check. Print warning.
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
if (!oat_file_assistant.HasOriginalDexFiles()) {
// We need to fallback but don't have original dex files. We have to
// fallback to opening the existing oat file. This is potentially
// unsafe so we warn about it.
accept_oat_file = true;
LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "
<< "Allow oat file use. This is potentially dangerous.";
} else {
// We have to fallback and found original dex files - extract them from an APK.
// Also warn about this operation because it's potentially wasteful.
LOG(WARNING) << "Found duplicate classes, falling back to extracting from APK : "
<< dex_location;
LOG(WARNING) << "NOTE: This wastes RAM and hurts startup performance.";
}
} else {
// TODO: We should remove this. The fact that we're here implies -Xno-dex-file-fallback
// was set, which means that we should never fallback. If we don't have original dex
// files, we should just fail resolution as the flag intended.
if (!oat_file_assistant.HasOriginalDexFiles()) {
accept_oat_file = true;
}
LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to "
" load classes for " << dex_location;
}
LOG(WARNING) << error_msg;
}
if (accept_oat_file) {
VLOG(class_linker) << "Registering " << oat_file->GetLocation();
//冲突检测通过之后把oat_file注册给source_oat_file
source_oat_file = RegisterOatFile(std::move(oat_file));
*out_oat_file = source_oat_file;
}
}
std::vector<std::unique_ptr<const DexFile>> dex_files;
// Load the dex files from the oat file.
if (source_oat_file != nullptr) {
bool added_image_space = false;
if (source_oat_file->IsExecutable()) {
//kEnableAppImage为true,
std::unique_ptr<gc::space::ImageSpace> image_space =
kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr;
if (image_space != nullptr) {
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> h_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
// Can not load app image without class loader.
if (h_loader != nullptr) {
std::string temp_error_msg;
// Add image space has a race condition since other threads could be reading from the
// spaces array.
{
ScopedThreadSuspension sts(self, kSuspended);
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseAddRemoveAppImageSpace,
gc::kCollectorTypeAddRemoveAppImageSpace);
ScopedSuspendAll ssa("Add image space");
runtime->GetHeap()->AddSpace(image_space.get());
}
{
ScopedTrace trace2(StringPrintf("Adding image space for location %s", dex_location));
//.art能直接获取内存镜像,直接通过classLinker加载
added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),
h_loader,
dex_elements,
dex_location,
/*out*/&dex_files,
/*out*/&temp_error_msg);
}
if (added_image_space) {
// Successfully added image space to heap, release the map so that it does not get
// freed.
image_space.release();
} else {
LOG(INFO) << "Failed to add image file " << temp_error_msg;
dex_files.clear();
{
ScopedThreadSuspension sts(self, kSuspended);
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseAddRemoveAppImageSpace,
gc::kCollectorTypeAddRemoveAppImageSpace);
ScopedSuspendAll ssa("Remove image space");
runtime->GetHeap()->RemoveSpace(image_space.get());
}
// Non-fatal, don't update error_msg.
}
}
}
}
if (!added_image_space) {
DCHECK(dex_files.empty());
//按OatFile->OatDexFile->DexFile 的顺序获取DexFile, 其实也就是从Oat文件中获取DexFile内容
dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
}
if (dex_files.empty()) {
error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation());
}
}
// 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;
//直接打开原始dex文件
if (!DexFile::Open(
dex_location, dex_location, 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;
}
方法很长,这里通过一个图简单总结下核心流程:
OatFileManager::OpenDexFilesFromOat核心流程
上图总结的逻辑已经非常清晰:
1.如果本地没有优化后的.odex文件,或者有但是失效了,那么先进行dex2oat编译。(Android Q之后,dex2oat编译这一步去掉了)
2.打开本地优化后的.odex文件,封装为OatFile
3.通过三种方式获取DexFile集合,优先级依次为:
-
.art获取
ClassLinker加载ImageSpace -
.odex获取
OatFile->OatDexFile->DexFile .dex获取
二、DexFile::Open()过程
LoadDexFiles()最终也会走Dex::Open(),但是它与直接打开原始dex文件的Dex::Open()是两个不同的重载方法。下面来简单看看:
2.1 LoadDexFiles走的Dex::Open():
art/runtime/oat_file_assistant.cc
std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
const OatFile& oat_file, const char* dex_location) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
// Load the main dex file.
std::string error_msg;
//通过oat_file获取oat_dex_file
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
dex_location, nullptr, &error_msg);
if (oat_dex_file == nullptr) {
LOG(WARNING) << error_msg;
return std::vector<std::unique_ptr<const DexFile>>();
}
//通过oat_dex_file获取dex_file
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file.get() == nullptr) {
LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
return std::vector<std::unique_ptr<const DexFile>>();
}
dex_files.push_back(std::move(dex_file));
// Load the rest of the multidex entries
for (size_t i = 1; ; i++) {
std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
if (oat_dex_file == nullptr) {
// There are no more multidex entries to load.
break;
}
dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file.get() == nullptr) {
LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
return std::vector<std::unique_ptr<const DexFile>>();
}
dex_files.push_back(std::move(dex_file));
}
return dex_files;
}
art/runtime/oat_file.cc
std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
ScopedTrace trace(__PRETTY_FUNCTION__);
static constexpr bool kVerify = false;
static constexpr bool kVerifyChecksum = false;
return DexFile::Open(dex_file_pointer_,
FileSize(),
dex_file_location_,
dex_file_location_checksum_,
this,
kVerify,
kVerifyChecksum,
error_msg);
}
art/runtime/dex_file.cc
std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base,//dex的开始
size_t size,//dex size
const std::string& location,//地址
uint32_t location_checksum,
const OatDexFile* oat_dex_file,//从oat_dex_file找的dex_file
bool verify,
bool verify_checksum,
std::string* error_msg) {
ScopedTrace trace(std::string("Open dex file from RAM ") + location);
return OpenCommon(base,
size,
location,
location_checksum,
oat_dex_file,
verify,
verify_checksum,
error_msg);
}
std::unique_ptr<DexFile> DexFile::OpenCommon(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
bool verify_checksum,
std::string* error_msg,
VerifyResult* verify_result) {
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifyNotAttempted;
}
//初始化DexFile对象
std::unique_ptr<DexFile> dex_file(new DexFile(base,
size,
location,
location_checksum,
oat_dex_file));
if (dex_file == nullptr) {
*error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
error_msg->c_str());
return nullptr;
}
if (!dex_file->Init(error_msg)) {
dex_file.reset();
return nullptr;
}
//Verify验证
if (verify && !DexFileVerifier::Verify(dex_file.get(),
dex_file->Begin(),
dex_file->Size(),
location.c_str(),
verify_checksum,
error_msg)) {
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifyFailed;
}
return nullptr;
}
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifySucceeded;
}
return dex_file;
}
因为前面OatFile已经加载进内存,这里直接从OatFile中获取dex内容并进行封装。
2.2 打开原始Dex文件走的Dex::Open():
bool DexFile::Open(const char* filename,
const std::string& location,
bool verify_checksum,
std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files) {
ScopedTrace trace(std::string("Open dex file ") + std::string(location));
DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
uint32_t magic;
File fd = OpenAndReadMagic(filename, &magic, error_msg);//打开文件
if (fd.Fd() == -1) {
DCHECK(!error_msg->empty());
return false;
}
//对zip和dex两种类型分别进行处理,前者可能包含多个dex
if (IsZipMagic(magic)) {
return DexFile::OpenZip(fd.Release(), location, verify_checksum, error_msg, dex_files);
}
if (IsDexMagic(magic)) {
std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.Release(),
location,
/* verify */ true,
verify_checksum,
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'", filename);
return false;
}
DexFile::OpenZip和DexFile::OpenFile,都需要将dex文件先加载到内存中,区别是前者可能有多个dex,之后会统一走OpenCommon,这与前面一致,就不赘述了。
参考:
https://blog.csdn.net/threepigs/article/details/52789614
https://blog.csdn.net/doon/article/details/76034383
https://www.52pojie.cn/forum.php?mod=viewthread&tid=1121372
网友评论