上一篇《深入理解ART虚拟机—虚拟机的启动》分析了art虚拟机的启动过程,不过跳过了一个技术点,就是ImageSpace。由之前的分析可知,ClassLinker是用来加载类对象的,有三类类对象,一是ClassRoot,包括java.lang包下的类,这类的类对象是语言自己的,不需要import,ART虚拟机是定义在mirror目录下的。二是boot_class_path,包括jdk和android.jar,这类的类对象是通过指定class path或者系统的Image文件。三是apk自己的dex/oat。ClassLinker的InitFromImage是通过ImageSpace来初始化ClassRoot和boot class path,InitWithoutImage的ClassRoot是直接AllocClass,boot class path是由直接由class path的DexFile生成DexCache加入dex_caches来生成的。第三类类对象是和应用自身相关的,都是走DexCache。
Runtime初始化的时候会创建gc::Heap对象,在Heap的构造函数会创建ImageSpace的实例。
auto* image_space = space::ImageSpace::Create(image_file_name.c_str(), image_instruction_set, &error_msg);
ImageSpace::Create是ImageSpace的静态构造方法,先找到image文件的路径,之后会生成load镜像文件,具体代码如下:
ImageSpace* ImageSpace::Create(const char* image_location,
const InstructionSet image_isa,
std::string* error_msg) {
std::string system_filename;
bool has_system = false;
std::string cache_filename;
bool has_cache = false;
bool dalvik_cache_exists = false;
bool is_global_cache = true;
const bool found_image = FindImageFilename(image_location, image_isa, &system_filename,
&has_system, &cache_filename, &dalvik_cache_exists,
&has_cache, &is_global_cache);
if (Runtime::Current()->IsZygote()) {
MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
}
ImageSpace* space;
bool relocate = Runtime::Current()->ShouldRelocate();
bool can_compile = Runtime::Current()->IsImageDex2OatEnabled();
if (found_image) {
const std::string* image_filename;
bool is_system = false;
bool relocated_version_used = false;
if (relocate) {
if (!dalvik_cache_exists) {
*error_msg = StringPrintf("Requiring relocation for image '%s' at '%s' but we do not have "
"any dalvik_cache to find/place it in.",
image_location, system_filename.c_str());
return nullptr;
}
if (has_system) {
if (has_cache && ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
// We already have a relocated version
image_filename = &cache_filename;
relocated_version_used = true;
} else {
// We cannot have a relocated version, Relocate the system one and use it.
std::string reason;
bool success;
// Check whether we are allowed to relocate.
if (!can_compile) {
reason = "Image dex2oat disabled by -Xnoimage-dex2oat.";
success = false;
} else if (!ImageCreationAllowed(is_global_cache, &reason)) {
// Whether we can write to the cache.
success = false;
} else {
// Try to relocate.
success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &reason);
}
if (success) {
relocated_version_used = true;
image_filename = &cache_filename;
} else {
*error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s': %s",
image_location, system_filename.c_str(),
cache_filename.c_str(), reason.c_str());
// We failed to create files, remove any possibly garbage output.
// Since ImageCreationAllowed was true above, we are the zygote
// and therefore the only process expected to generate these for
// the device.
PruneDalvikCache(image_isa);
return nullptr;
}
}
} else {
CHECK(has_cache);
// We can just use cache's since it should be fine. This might or might not be relocated.
image_filename = &cache_filename;
}
} else {
if (has_system && has_cache) {
// Check they have the same cksum. If they do use the cache. Otherwise system.
if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
image_filename = &cache_filename;
relocated_version_used = true;
} else {
image_filename = &system_filename;
is_system = true;
}
} else if (has_system) {
image_filename = &system_filename;
is_system = true;
} else {
CHECK(has_cache);
image_filename = &cache_filename;
}
}
{
// Note that we must not use the file descriptor associated with
// ScopedFlock::GetFile to Init the image file. We want the file
// descriptor (and the associated exclusive lock) to be released when
// we leave Create.
ScopedFlock image_lock;
image_lock.Init(image_filename->c_str(), error_msg);
VLOG(startup) << "Using image file " << image_filename->c_str() << " for image location "
<< image_location;
// If we are in /system we can assume the image is good. We can also
// assume this if we are using a relocated image (i.e. image checksum
// matches) since this is only different by the offset. We need this to
// make sure that host tests continue to work.
space = ImageSpace::Init(image_filename->c_str(), image_location,
!(is_system || relocated_version_used), error_msg);
}
if (space != nullptr) {
return space;
}
if (relocated_version_used) {
// Something is wrong with the relocated copy (even though checksums match). Cleanup.
// This can happen if the .oat is corrupt, since the above only checks the .art checksums.
// TODO: Check the oat file validity earlier.
*error_msg = StringPrintf("Attempted to use relocated version of %s at %s generated from %s "
"but image failed to load: %s",
image_location, cache_filename.c_str(), system_filename.c_str(),
error_msg->c_str());
PruneDalvikCache(image_isa);
return nullptr;
} else if (is_system) {
// If the /system file exists, it should be up-to-date, don't try to generate it.
*error_msg = StringPrintf("Failed to load /system image '%s': %s",
image_filename->c_str(), error_msg->c_str());
return nullptr;
} else {
// Otherwise, log a warning and fall through to GenerateImage.
LOG(WARNING) << *error_msg;
}
}
if (!can_compile) {
*error_msg = "Not attempting to compile image because -Xnoimage-dex2oat";
return nullptr;
} else if (!dalvik_cache_exists) {
*error_msg = StringPrintf("No place to put generated image.");
return nullptr;
} else if (!ImageCreationAllowed(is_global_cache, error_msg)) {
return nullptr;
} else if (!GenerateImage(cache_filename, image_isa, error_msg)) {
*error_msg = StringPrintf("Failed to generate image '%s': %s",
cache_filename.c_str(), error_msg->c_str());
// We failed to create files, remove any possibly garbage output.
// Since ImageCreationAllowed was true above, we are the zygote
// and therefore the only process expected to generate these for
// the device.
PruneDalvikCache(image_isa);
return nullptr;
} else {
// Check whether there is enough space left over after we have generated the image.
if (!CheckSpace(cache_filename, error_msg)) {
// No. Delete the generated image and try to run out of the dex files.
PruneDalvikCache(image_isa);
return nullptr;
}
// Note that we must not use the file descriptor associated with
// ScopedFlock::GetFile to Init the image file. We want the file
// descriptor (and the associated exclusive lock) to be released when
// we leave Create.
ScopedFlock image_lock;
image_lock.Init(cache_filename.c_str(), error_msg);
space = ImageSpace::Init(cache_filename.c_str(), image_location, true, error_msg);
if (space == nullptr) {
*error_msg = StringPrintf("Failed to load generated image '%s': %s",
cache_filename.c_str(), error_msg->c_str());
}
return space;
}
}
FindImageFilename会查找系统和data区是否有image file,系统区镜像的目录是/system/framework/<image_isa>/boot.art,data区的image file的路径是/data/dalvik-cache/<image_isa>/boot.art。/system 下面的是在系统区,是ROM带的,而/data 下的image file是系统运行时生成的,GenerateImage就是生成这个cache image file的函数。image file存在的情况下,就能调用ImageSpace::Init来初始化ImageSpace了。
ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_location,
bool validate_oat_file, std::string* error_msg) {
CHECK(image_filename != nullptr);
CHECK(image_location != nullptr);
uint64_t start_time = 0;
if (VLOG_IS_ON(heap)