写在开头:本篇为《深入理解Android:Java虚拟机ART》ART 启动相关的读书笔记。
由于书是以Android7代码去分析的,因此下文的代码链接也都是Android7的代码。
ART虚拟机是一个非常庞大和复杂的系统。学习这祥的一个系统, 我们必须要多方面、 多角度来研究它。这里先研究ART虚拟机的启动流程。
在Android系统中 , Java虚拟机是借由大名鼎鼎的Zygote进程来创建的 。Zygote是Java 世界的创造者一即Android中所有Java进程都由Zygote进程fork而来, 而Zygote进程自己又是Linux系统上的init进程通过解析配置脚本来启动的。假设目标设备为32位CPU架构, zygote进程对应的配置脚本文件是system/core/rootdir/init.zygote32.rc, 该文件描述了init该如何启动zygote进程。
文件位置:
http://aospxref.com/android-7.1.2_r39/xref/system/core/rootdir/init.zygote32.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks
这里只关注第一行的内容。
- service zygote:它告诉init进程, 现在我们要配置一个名为zygote的服务 服务是init的内部概念, 可以不用管它。
- /system/bin/app_process :指明 zygote服务对应的二进制文件路径 init 创建服务的处理逻辑很简单. 就是fork 一个子进程来运行指定的程序。 对zygote 服务而言, 这个程序就是system/bin/app_process。
- -Xzygote /system/bin --zygote --start-system-server :这一串内容是传递给app_process的启动参数
由上文的描述可知 zygote进程对应的程序实际上是system/bin/app_process。
http://aospxref.com/android-7.1.2_r39/xref/frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
...
//AppRuntime是一个类,定义在app_main.cpp涓中。其基类是AndroidRuntime
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...
while (i < argc) {
const char* arg = argv[i++];
//zygote脚本里的启动参数为
//"-Xzygote /systern/bin --Zygote --start-system-server"
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
//niceNarne将被设置为app_process的进程名 , 32位机对应的进程名 为“zygote",而64位机上该进程名为"zygote64"
niceName = ZYGOTE_NICE_NAME;
}
...
}
...
if (zygote) {
//调用基类AndroidRuntime的start函数 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10;
}
}
接着来看AndroidRuntime的start函数。
http://aospxref.com/android-7.1.2_r39/xref/frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
...
//重点关注JniInvocation.Init函数和startVm函数,它们和启动ART虚拟机有关
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
...
}
上述代码中和启动 ART 虚拟机密切相关的两个重要函数如下所示。
- JniInvocation的Init函数:它将加载ART虚拟机的核心动态库
- AndroidRuntime的startVm函数:在ART虚拟机对应的核心动态库加载到Zygote进程后,该函数将启动ART虚拟机。
1.1 JniInvocation lnit 函数介绍
http://aospxref.com/android-7.1.2_r39/xref/libnativehelper/JniInvocation.cpp
bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__
char buffer[PROP_VALUE_MAX];
#else
char* buffer = NULL;
#endif
//art核心库是通过动态加载so的方式加载到zygote进程的,GetLibrary根据情况返回
//目标so的文件名,正常情况下加载的art核心动态库文件名为libart.so
library = GetLibrary(library, buffer);
const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
handle_ = dlopen(library, kDlopenFlags);
...
//从libart.so中找到 JNI_GetDefaultJavaVMInitArgs 函数的地址(也就是函数指针),将该地址存储到JNI_GetDefaultJavaVMInitArgs_中
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
"JNI_GetDefaultJavaVMInitArgs")) {
return false;
}
//从libart.so中找到 JNI_CreateJavaVM 函数,它就是创建虚拟机的入口函数。该函数的地址保存在JNI_CreateJavaVM_变量中
if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) {
return false;
}
//从libart.so中找到 JNI_GetCreatedJavaVMs 函数
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
"JNI_GetCreatedJavaVMs")) {
return false;
}
return true;
}
从libart.so里取出并保存三个函数的函数指针:
- 这三个函数的代码位于java_vm_ext.cc中。
- JNI_CreateJavaVM 用于创建Java虚拟机,所以它是最关键的。
1.2 AndroidRuntime startVm函数介绍
接着来看 AndroidRuntime::startVm。
http://aospxref.com/android-7.1.2_r39/xref/frameworks/base/core/jni/AndroidRuntime.cpp
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
JavaVMInitArgs initArgs;
.... //这段省略的代码非常长. 其主要功能是为ART虚拟机准备启动参数
/*
* Initialize the VM.
*
* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
//调用JNI_CreateJavaVM。注意,该函数和JniInvocation Init 从 libart.so取出的 JNI_CreateJavaVM 函数同名,但他们是不同的函数。
//JniInvocation Init 取出的那个函数的地址保存在 JNI_CreateJavaVM_ (其名称后多一个下划线)变量中,这一点容易混淆
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
return 0;
这里书中有一点错误,JNI_CreateJavaVM 不是定义在 AndroidRuntime.cpp 中,而是 JniInvocation.cpp 中。
如上述代码中的注释所言,JNI_CreateJavaVM 函数并非是JniInovcation Init从libart.so获取的那个JNI_CreateJavaVM函数。 相反,它是直接在AndroidRuntime.cpp中定义的。
http://aospxref.com/android-7.1.2_r39/xref/libnativehelper/JniInvocation.cpp
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
//调用JniInvocation 中的 JNI_CreateJavaVM,而JniInvocation又会调用 libart.so 中的JNI_CreateJavaVM函数。
return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
}
这里和 libart.so 关联上了。下面来看 libart.so 中的这个 JNI_CreateJavaVM 函数。
文件位置:
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/java_vm_ext.cc#939
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
ScopedTrace trace(__FUNCTION__);
const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
...//为虚拟机准备参数
//创建Runtime对象,它是ART虚拟机的化神
if (!Runtime::Create(options, ignore_unrecognized)) {
return JNI_ERR;
}
//加载其他关键动态库,它们的文件路径由/etc/public.libraries.txt文件描述
android::InitializeNativeLoader();
//获取刚创建的Runtime对象
Runtime* runtime = Runtime::Current();
/启动Runtime对象
bool started = runtime->Start();
...
//获取JNI Env 和 Java VM 对象
*p_env = Thread::Current()->GetJniEnv();
*p_vm = runtime->GetJavaVM();
return JNI_OK;
}
在上述libart.so的JNI_CreateJavaVM代码中,我们见到了ART 虚拟机的化身Runtime(即ART虚拟机在代码中是由Runtime类来表示的)。其中:
- Runtime Create 将创建一个Runtime 对象。
- Runtime Start 函数将启动这个Runtime对象,也就是启动Java虚拟机。
来看看Runtime 对象的创建。
VM和Runtime
虚拟机一词英文为Virtual Machine,Runtime则是另一个在虚拟机技术领域常用于表示虚拟机的单词。Runtime也被翻译为运行时。在ART虚拟机Native层代码中,Runtime是一个类。而JDK源码里也有一个Runtime类(位于java.lang包下)。这个Java Runtime 类提供了一个针对整个虚拟机层面而言的API,比如exit(退出虚拟机)、gc(触发垃圾回收)、load(加载动态库)等。
2.Runtime Create介绍
2.1 Create函数介绍
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/runtime.cc
bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) {
RuntimeArgumentMap runtime_options;
return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) &&
Create(std::move(runtime_options));
}
虚拟机是一个复杂系统,所以它有很多控制参数。创建Runtime时,调用者将这些参数信息放在本函数的入参 raw_options 对象中,该对象的类型是RuntimeOptions。
不过,Runtime内部却使用类型为RuntimeArgumentMap的对象来存储参数。 下面这段代码中,ParseOptions 函数将存储在 raw_options 里的参数信息提取井保存到 runtime_options 对象里, 而 runtime_options 的类型就是 RuntimeArgumentMap 。
上述代码中
- ParseOptions 先将外部调用者传入的参数信息(保存在 raw_options 中)提取并保存到 runtime_options 里
- 然后再用 runtime_options 作为入参来创建 (Create) 一个 Runtime 对象
ART 虚拟机所需的参数信息(包括参数名、 参数类型、默认值)都定义在 runtime_options.def 文件中。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/runtime_options.def
RUNTIME_OPTIONS_KEY (Unit, Zygote)
RUNTIME_OPTIONS_KEY (Unit, Help)
RUNTIME_OPTIONS_KEY (Unit, ShowVersion)
RUNTIME_OPTIONS_KEY (std::string, BootClassPath)
RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPathLocations) // std::vector<std::string>
RUNTIME_OPTIONS_KEY (std::string, ClassPath)
RUNTIME_OPTIONS_KEY (std::string, Image)
RUNTIME_OPTIONS_KEY (Unit, CheckJni)
RUNTIME_OPTIONS_KEY 定义了一个虚拟机控制参数。
了解 RuntimeOptions 后,接着来看 Create 函数 。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/runtime.cc
bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {
// TODO: acquire a static mutex on Runtime to avoid racing.
//一个虚拟机进程中只有一个Runtime对象,名为instance_,采用单例方式来创建
if (Runtime::instance_ != nullptr) {
return false;
}
//创建Runtime对象
instance_ = new Runtime;
//用保存了虚拟机控制参数信息的 runtime_options 来初始化 这个 runtime 对象
//重点来看Init函数
if (!instance_->Init(std::move(runtime_options))) {
// TODO: Currently deleting the instance will abort the runtime on destruction. Now This will
// leak memory, instead. Fix the destructor. b/19100793.
// delete instance_;
instance_ = nullptr;
return false;
}
return true;
}
2.2 Init函数介绍
Init 是 runtime 创建过程中最核心的函数。ART虚拟机中大部分重要模块和关键数据结构都将亮相。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/runtime.cc
bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
...
RuntimeArgumentMap runtime_options(std::move(runtime_options_in));
//关键模块之 MemMap : 用于管理内存映射。 ART 大量使用了内存映射技术。比如 .oat 文件就会通过 mmap 映射到虚拟机进程的虚拟内存中来。
MemMap::Init();
//关键模块之 OatFileManager : art 虚拟机 会打开多个 oat 文件,通过该模块可统一管理它们
oat_file_manager_ = new OatFileManager;
//关键模块之 Monitor :和Java 中的 Monitor 有关,用于实现线程同步的模块。
Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold));
...
// 关键模块之 Heap : heap 是 art 虚拟机中非常重要的模块
heap_ = new gc::Heap(...);
...
//关键模块之 JavaVmExt :JavaVmExt 就是 JNI 中代码 Java 虚拟机 的对象,其基类为JavaVM,真实类型为 JavaVmExt。根据 JNI 规范,一个进程只有唯一的一个 JavaVm 对象。对 ART 虚拟机来说,这个 JavaVm 对象就是此处的 java_vm_
java_vm_ = new JavaVMExt(this, runtime_options);
//关键模块之 Thread : Thread 是虚拟机中代表线程的类,下面两个函数调用 Thread 类的 Startup 和 Attach 以初始化虚拟机主线程
Thread::Startup();
Thread* self = Thread::Attach("main", false, nullptr, false);
...
//关键模块之 ClassLinker :ClassLinker 也是非常重要的模块。从其命名可以看出,它处理和 class 有关的工作,比如解析某个类、寻找某个类等。
class_linker_ = new ClassLinker(intern_table_);
...
}
目标是了解虚拟机的启动流程,其他的细节暂时忽略,大致过一遍。
2.2.1 MemMap 和 OatFileManager 模块
MemMap
MemMap 是一个辅助工具类, 它封装了和内存映射(memory map) 有关的操作。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/mem_map.h#53
class MemMap {
//每一个MemMap对象都有一个名字
const std::string& GetName() const {
return name_;
}
//针对这块映射内存的保护措施, 比如是否可读、可写、可执行等
int GetProtect() const {
return prot_;
}
/*ART对内存使用非常计较. 很多地方都使用了内存监控技术比如使用STL标准容器类, ART将
监控容器里内存分配的情况。当然, 监控内存分配是有代价的, 所以ART设计了一个编译常量
(constexpr)来控制是否启用内存监控。下而这行代码中. Maps是 AllocationTrackingMultiMap 类型的别名, AllocationTrackingMultiMap真实类型是
multimap , AllocationTracking 即是TrackAllocation (跟踪分配)之意*/
typedef AllocationTrackingMultiMap<void*, MemMap*, kAllocatorTagMaps> Maps;
}
/*ART对线程安全的问题也是倍加小心比如,下面这行代码在声明-个名为 maps_ 的静态成员变量之
后, 还使用了一个 GUARDED_BY 宏,宏里有一个参数。从其命名可以看出, mem_maps_lock_ 该是
一个互斥锁所以,第一次看到这行代码的读者可能会猜测, 这是想表达使用maps_ 时需要先拿到
mem_maps_lock_ 锁的意思吧?没错*/
static Maps* maps_ GUARDED_BY(Locks::mem_maps_lock_);
/*
MapAnonymous :用于映射一块匿名内存, use_ashmen 默认为 true ,
表示将从 android 系统里特有的 ashmem 设备(一种用于进程间共享内存的虚拟设备。
ashmem是 anonymous shared memory 的简写,表示匿名共享内存,是 android 系统上常用的进程间共享内存的方法)中分配并映射内存。
*/
static MemMap* MapAnonymous(const char* name,
uint8_t* addr,
size_t byte_count,
int prot,
bool low_4gb,
bool reuse,
std::string* error_msg,
bool use_ashmem = true);
// MapFile:将其个文件映射到内存
static MemMap* MapFile(size_t byte_count,
int prot,
int flags,
int fd,
off_t start,
bool low_4gb,
const char* filename,
std::string* error_msg) {
return MapFileAtAddress(nullptr,
byte_count,
prot,
flags,
fd,
start,
/*low_4gb*/low_4gb,
/*reuse*/false,
filename,
error_msg);
}
OatFileManager
OatFileManager 用于管理虚拟机加载的oat文件dex字节码编译成机器码后,相关内容会存储在一个以oat为后缀名的文件里。我们先来简单认识OAT文件的格式。
OAT文件格式简介:
OAT文件包含一个OatHeader头结构。注意.这个OatHeader信息并不存储在OAT文件的头部。OAT文件其实是一个ELF格式的文件,相关信息存储在ELF对应的段中。
Oat文件是怎么来的呢?它是对jar包或apk包中的dex项(名为classes.dex、classes2.dex、classes3.dex等.其实就是dex文件打包到jar或apk里了。以后我们统称它们为dex文件.而不必理会它们是单独的.dex文件还是jar或apk包中的一项)进行编译处理后得到的。jar或apk中可包含多个dex项(即所谓的multidex),每一个jar或apk中的所有dex文件在oat文件中对应都有一个OatDexFile项。 OatDexFile项存储了一些信息,比如它所对应的dex文件的路径、dex文件的校验以及其他各种信息在oat文件中的位置(offset)等。

OAT文件里的一个DexFile项包含一个.dex文件的全部内容。通过在OAT文件中包含dex文件的内容,ART虚拟机只需要加载OAT文件即可获取相关信息,而不需要单独再打开dex文件了。当然,这种做法也使得OAT文件尺寸较大。
OatFileManager类介绍
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/oat_file_manager.h#46
class OatFileManager {
public:
OatFileManager() : have_non_pic_oat_file_(false) {}
~OatFileManager();
// 往OatManager中添加一个OatFile对象
const OatFile* RegisterOatFile(std::unique_ptr<const OatFile> oat_file)
/* ART虚拟机会加载多个OAT文件。其中:
(1) zygote作为第一个Java进程会首先加载一些基础与核心的oat文件。这些oat文件里包含了Android系统中所有Java程序所依赖的基础功能类(比如Java标准类)。这些oat文件称之为boot oat文件(与之对应的一个名词叫boot image。下文将详细介绍boot image的相关内
容)
(2) APP进程通过zygote fork得到,然后该APP进程将加栽APK包经过dex2oat得到的oat文件。
该APP对应的oat文件叫app image。下面的 GetBootOatFiles 将返回 boot oat 文件信息.
返回的是一个vector数组*/
std::vector<const OatFile*> GetBootOatFiles() const;
}
/* 将包含在boot镜像里的oat文件信息注册到OatFileManager中。ImageSpace代表一个由映射内
存构成的区域(Space), zygote启动后,会将boot oat文件通过memap映射到内存,从而得到
一个ImageSpace。下面将详细介绍和 ImageSpace 相关的内容*/
std::vector<const OatFile*> RegisterImageOatFiles(std::vector<gc::space::ImageSpace*> spaces)
// 从Oat文件中找到指定的dex文件。dex文件由DexFile对象表示
std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat
往OatFileManager中注册Oatfile对象非常简单,就是将OatFile对象保存到oat files_ (对象为std set)容器中
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/oat_file_manager.cc?fi=RegisterOatFile#RegisterOatFile
const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) {
WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
DCHECK(oat_file != nullptr);
if (kIsDebugBuild) {
CHECK(oat_files_.find(oat_file) == oat_files_.end());
for (const std::unique_ptr<const OatFile>& existing : oat_files_) {
CHECK_NE(oat_file.get(), existing.get()) << oat_file->GetLocation();
// Check that we don't have an oat file with the same address. Copies of the same oat file
// should be loaded at different addresses.
CHECK_NE(oat_file->Begin(), existing->Begin()) << "Oat file already mapped at that location";
}
}
have_non_pic_oat_file_ = have_non_pic_oat_file_ || !oat_file->IsPic();
const OatFile* ret = oat_file.get();
oat_files_.insert(std::move(oat_file));
return ret;
}
关于boot oat 文件里所包含的系统基础类。frameworks/base 下有一个preloaded-classes 文件. 其内容是希望加载到zygote 进程里的类名(按照JNI 格式定义), 这些类包含在不同的boot oat 文件里。
http://aospxref.com/android-7.1.2_r39/xref/frameworks/base/preloaded-classes
[B
[C
[D
[F
[I
[J
[Landroid.accounts.Account;
[Landroid.animation.Animator;
[Landroid.animation.Keyframe$FloatKeyframe;
[Landroid.animation.Keyframe$IntKeyframe;
[Landroid.animation.Keyframe$ObjectKeyframe;
[Landroid.animation.Keyframe;
[Landroid.animation.PropertyValuesHolder;
[Landroid.app.LoaderManagerImpl;
[Landroid.content.ContentProviderResult;
[Landroid.content.ContentValues;
[Landroid.content.Intent;
...
[Ljava.lang.Short;
[Ljava.lang.StackTraceElement;
[Ljava.lang.String;
[Ljava.lang.Thread$State;
[Ljava.lang.Thread;
[Ljava.lang.ThreadGroup;
[Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;
[Ljava.lang.Throwable;
...
由于zygote 是Java 世界的第一个进程, 其他
APP 进程(包括system server 进程)均由zygote 进程fork 而来所以:
- 这些加载到zygote 进程里的类也叫预加载类, 即所谓的preloaded classes。
- 根据linux 进程fork 的机制,其他APP 进程从zygote fork 后, 将继承得到这些预加载的类。
fork采用的是copy-on-write,调用一次,返回两次。当父子进程任一方修改内存数据时(这是on-write时机),才发生缺页中断,从而分配新的物理内存(这是copy操作)。
fork()之后,kernel把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。当其中某个进程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断,kernel就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。
2.2.2 Thread 模块
来看看和 art 虚拟机执行密切相关的 Thread 类。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/runtime.cc
java_vm_ = new JavaVMExt(this, runtime_options);
Thread::Startup();
Thread* self = Thread::Attach("main", false, nullptr, false);
Thread 中的 Startup 和 Attach函数,重点是 Attach 函数。
Startup 函数介绍
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/thread.cc#1557
void Thread::Startup() {
CHECK(!is_started_);
is_started_ = true;
{
// MutexLock to keep annotalysis happy.
//
// Note we use null for the thread because Thread::Current can
// return garbage since (is_started_ == true) and
// Thread::pthread_key_self_ is not yet initialized.
// This was seen on glibc.
MutexLock mu(nullptr, *Locks::thread_suspend_count_lock_);
resume_cond_ = new ConditionVariable("Thread resumption condition variable",
*Locks::thread_suspend_count_lock_);
}
// pthread_key_create将被调用
//该函数将创建一块调用线程特有的数据区域. 即所谓的Thread Local Storage (简写为TLS)
// Allocate a TLS slot.
CHECK_PTHREAD_CALL(pthread_key_create, (&Thread::pthread_key_self_, Thread::ThreadExitCallback),
"self key");
// Double-check the TLS slot allocation.
if (pthread_getspecific(pthread_key_self_) != nullptr) {
LOG(FATAL) << "Newly-created pthread TLS slot is not nullptr";
}
}
Attach函数介绍
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/thread.cc
Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_group,
bool create_peer) {
Runtime* runtime = Runtime::Current();
...
Thread* self;
{
MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_);
...
Runtime::Current()->StartThreadBirth();
// 关键函数之一 构造函数
self = new Thread(as_daemon);
// 关键函数之二
bool init_success = self->Init(runtime->GetThreadList(), runtime->GetJavaVM());
Runtime::Current()->EndThreadBirth();
...
}
// 关键函数之三
self->InitStringEntryPoints();
CHECK_NE(self->GetState(), kRunnable);
self->SetState(kNative);
...
return self;
}
Attach内部包含三个关键函数,先看第一个,Thread的构造函数。
Thread的构造函数
主要是完成对某些成员变量的初始化。
tlsPtr_ 是Thread 类中非常关键的结构体。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/thread.h
struct PACKED(sizeof(void*)) tls_ptr_sized_values {
tls_ptr_sized_values() : card_table(nullptr), exception(nullptr), stack_end(nullptr),
managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), tmp_jni_env(nullptr),
self(nullptr), opeer(nullptr), jpeer(nullptr), stack_begin(nullptr), stack_size(0),
stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr),
top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr),
instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr),
frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0),
last_no_thread_suspension_cause(nullptr), thread_local_objects(0),
thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr),
mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr),
thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr),
nested_signal_state(nullptr), flip_function(nullptr), method_verifier(nullptr),
thread_local_mark_stack(nullptr) {
std::fill(held_mutexes, held_mutexes + kLockLevelCount, nullptr);
}
...
uint8_t* stack_end;
//和本线程关联的Jni环境对象
JNIEnvExt* jni_env;
...
//指向包含本对象的Thread对象
Thread* self;
//下面两个成员变量也和线程栈有关。详情见下文对线程栈的介绍
uint8_t* stack_begin;
// Size of the stack.
size_t stack_size;
...
//jni_entrypoints :结构体,和JNI调用有关。里边只有一个函数指针成员变量,名为 pDlsymLookup。当 JNI 函数未注册时,这个成员变量将被调用以找到目标 JNI 函数。
JniEntryPoints jni_entrypoints;
//quick_entrypoints :结构体,其成员变量全是函数指针类型,其定义可参考 quick_entrypoints_list.h
//它包含了一些由ART虚拟机提供的某些功能,而我们编译得到的机器码可能会用到它们。生产机器码时,
//需要生成对应的调用指令以跳转到这些函数。
QuickEntryPoints quick_entrypoints;
...
} tlsPtr_;
接着来看关键函数之二 Init函数。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/thread.cc
bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) {
...
//设置tlsPtr_里的 jni_entrypoints 和 quick_entrypoints
InitTlsEntryPoints();
...
//下而这段代码将当前这个Thread对队设置到本线程的本地存储空间中去
#ifdef __ANDROID__
__get_tls()[TLS_SLOT_ART_THREAD_SELF] = this;
#else
CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, this), "attach self");
#endif
DCHECK_EQ(Thread::Current(), this);
tls32_.thin_lock_thread_id = thread_list->AllocThreadId(this);
//每一个线程将关联一个 JNIEnvExt对象。 它存储在 tlsPtr_jni_env 变量中。对主线程而言
// JNIEnvExt 对象由 Runtime 创建并传给代表主线程的Thread 对象,也就是此处的 Thread 对象
if (jni_env_ext != nullptr) {
DCHECK_EQ(jni_env_ext->vm, java_vm);
DCHECK_EQ(jni_env_ext->self, this);
tlsPtr_.jni_env = jni_env_ext;
} else {
tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm);
if (tlsPtr_.jni_env == nullptr) {
return false;
}
}
// Runtime对象中有一个 thread_list_ 成员变量,其类型是 ThreadList,用于存储虚拟机所创建的 Thread对象
thread_list->Register(this);
return true;
}
thread.cc这个类中有lnitStackHwm、InitCpu、InitTlsEntryPoints以及
InitInterpreterTls等init函数。
lnitStackHwm
本函数用于设置线程的线程栈。
Android平台上, 我们可通过调用pthread_ create来创建一个线程。
这里省略流程,直接贴出线程栈创建过程:
- mmap 得到一块内存,其返回值为该内存的低地址(stack_base)
- 设置该内存从低地址开始的某段区域(由guard_size)为不可访问。
- 得到该内存段的高地址, 将其作为线程栈的栈底位置传递给clone系统调用 。
再来看lnitStackHwm等。说实话这里我没有看懂。so,先略过。
接着来看关键函数之三 InitStringEntryPoints函数。
通过函数名我们可判断它也是初始化某些EntryPoints, 而且可能是和字符串相关某些函数。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/thread.cc
void Thread::InitStringEntryPoints() {
ScopedObjectAccess soa(this);
QuickEntryPoints* qpoints = &tlsPtr_.quick_entrypoints;
qpoints->pNewEmptyString = reinterpret_cast<void(*)()>(
soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newEmptyString));
qpoints->pNewStringFromBytes_B = reinterpret_cast<void(*)()>(
soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_B));
...
}
WellKnownClasses::java_lang_StringFactory_newEmptyString,这是一个静态变量,类型为 jmethodID (熟悉JNI的读者可能还记得,在JNI层中, jmethodID 用于表示一个Java方法,与之类似的是jFieldID,表示一个Java类中的成员)。 jmethodID 的真实数据类型并无标准规定,不同虚拟机实现会使用不同的数据类型。待会我们会看到ART虚拟机里的 jmethodID 到底是什么此处的 jmethodID 所表示的Java方法是 java.lang.StringFactor.newEmptyString 函数。
来看看ScopedObjectAccess 的 DecodeMethod 。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/scoped_thread_state_change.h?r=&mo=5941&fi=161#161
ArtMethod* DecodeMethod(jmethodID mid) const SHARED_REQUIRES(Locks::mutator_lock_) {
Locks::mutator_lock_->AssertSharedHeld(Self());
DCHECK(IsRunnable()); // Don't work
//数据类型转换,将输入的 jmethodID 转换成 ArtMethod*。也就是说,一个 jmethodID,实际上指向的是一个 ArtMethod 对象.
with raw objects in non-runnable states.
return reinterpret_cast<ArtMethod*>(mid);
}
StringFactory部分成员函数以及对应的jmethodID变量定义
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/well_known_classes.cc
jmethodID WellKnownClasses::java_lang_StringFactory_newEmptyString;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_B;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BI;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BII;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIII;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIIString;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BString;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIICharset;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BCharset;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromChars_C;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromChars_CII;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromChars_IIC;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromString;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromStringBuffer;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromCodePoints;
jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromStringBuilder;
2.2.3 Heap 模块
以先了解大概流程以及某些关键类的作用为主。
.art文件格式介绍
一个包含class.dex 项 的jar 或 apk 文件经由 dex2oat 进行编译处理后实际上会生成两个结果文件,一个是.oat文件,另一个是.art文件。
- .oat文件:class.dex 内容 将被完整拷贝到.oat文件里。
- .art文件:它就是ART虚拟机代码里经常提到的Image文件。
根据art文件的来源(比如它是从哪个jar包或apk包编译得来)。Image分boot镜像(boot image)和 app镜像(app image)。
- 来源于某个apk的art文件成为APP镜像
- 来自Android系统里的/framework下的那些jar包的art文件统称为boot镜像。
为什么叫Image?
首先,art文件加载到虚拟机中,都是通过mmap的方式去完成,其次,art文件的内容布局有严格组织,可以直接转换成对应的对象。
另外,一般而言,针对核心库的变异都会生成boot .art镜像文件,而针对app的编译则通过dex2oat相关选项来控制是否生成art文件。
创建ImageSpace对象
art文件将被映射到art虚拟机进程的内存里,该工作是通过ImageSpace的init函数完成的,其返回结果是一个ImageSpace对象。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/gc/space/image_space.cc
ImageSpace* ImageSpace::Init(const char* image_filename,
const char* image_location,
bool validate_oat_file,
const OatFile* oat_file,
std::string* error_msg) {
...
//总之,需牢记2点:第一, art虚拟机加载的是 dalvik-cache下的art文件。
//第二,dalvik-cache下的art文件和其他来源的art文件可能并不完全相同。某些情况下,虚拟机直接从 framework 等源目录下直接copy过来,有些情况下 虚拟机会对源art文件进行一些处理以提升安全性。
std::unique_ptr<File> file;
{
...
//打开 dalvik-cache 下的 art 文件 file.reset(OS::OpenFileForReading(image_filename));
...
}
ImageHeader temp_image_header;
ImageHeader* image_header = &temp_image_header;
{
//从该art文件中读取 ImageHeader,它位于文件的头部
bool success = file->ReadFully(image_header, sizeof(*image_header));
...
}
//获取该art文件的大小
const uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());
...
//取出ImageHeader的 section_ 数组中最后一个类型(kSectionImageBitmap)所对应的 ImageSection 对象。 这个section里存储的是位图空间的位置和大小。
const auto& bitmap_section = image_header->GetImageSection(ImageHeader::kSectionImageBitmap);
...
//现在准备将art文件map到内存
std::vector<uint8_t*> addresses(1, image_header->GetImageBegin());
...
//art 文件映射到内存后的 MemMap 对象就是它。
std::unique_ptr<MemMap> map;
std::string temp_error_msg;
for (uint8_t* address : addresses) {
...
if (storage_mode == ImageHeader::kStorageModeUncompressed) {
//将art文件映射到zygote进程的虚拟内存空间
map.reset(MemMap::MapFileAtAddress(address,
image_header->GetImageSize(),
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
file->Fd(),
0,
/*low_4gb*/true,
/*reuse*/false,
image_filename,
/*out*/out_error_msg));
}
...
// 创建一个ImageSpace对象.
// We only want the mirror object, not the ArtFields and ArtMethods.
std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename,
image_location,
map.release(),
bitmap.release(),
image_end));
...
// 返回所创建的space对象
return space.release();
}
总结:
- ART虚拟机的一个重要的特点就是大量使用映射内存。
- 一个.art文件加载到虚拟机进程的内存空间后对应一个ImageSpace对象。
Heap构造函数第一部分
Heap构造函数相当复杂,代码长达400多行。if/else等分支情况非常多。更有甚者,其
调用流程中甚至可能会启动dex2oat或patchoat等具他进程来做一些更为复杂的工作。这导致
Heap构造函数不仅代码复杂.而且执行时间也可能很长。先学习部分。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/gc/heap.cc
Heap::Heap(size_t initial_size,
size_t growth_limit,
size_t min_free,
size_t max_free,
... /* 非常多的参数 */):... /* 初始化列表也很长 */{
/*
Heap之所以复杂的原因是它负责管理 ART虚拟机
中各种内存资源(不同的Space)、GC(Garbage Collect,垃圾回收)模块、.art文件加载、Java指令编译后得到的机器码和虚拟内存相关模块交互等重要功能
*/
Runtime* const runtime = Runtime::Current();
const bool is_zygote = runtime->IsZygote();
...
//垃圾回收相关
ChangeCollector(desired_collector_type_);
//创建两个 HeapBitmap 对象
live_bitmap_.reset(new accounting::HeapBitmap(this));
mark_bitmap_.reset(new accounting::HeapBitmap(this));
// 遍历数组各个元素,加载每一个art文件,不过,该数组最开始只有一个元素,所以下面的for循环中将添加其他的art文件。注意,这只是针对boot镜像而言。
for (size_t index = 0; index < image_file_names.size(); ++index) {
/*
加载art文件,返回一个 ImageSpace 对象。CreateBootimage 的内容非常复杂。比
如,如果art文件不存在,则会fork一个子进程以执行dex2oat进行编译,假设本例所需的 art 文件已经就绪。那么,CreatBootimage 内部
将调用 ImageSpace 的 init 函数以创建 ImageSpace 对象。
*/
space::ImageSpace* boot_image_space = space::ImageSpace::CreateBootImage(
image_name.c_str(),
image_instruction_set,
index > 0,
&error_msg);
...
}
...
}
Heap构造函数, 它主要完成了boot镜像所需art文件的加载,然后得到一
系列的ImageSpace对象,最后再保存到Heap对应的成员变量中。
2.2.4 JavaVMExt 和 JNIEnvExt
本节讨论JNI中最常见的两个类JavaVM和JNIEnv。
- JavaVM在JNI层中表示Java虚拟机。它的作用有点像Runtime。只不过JNI作为一种规范, 它必须设定一个统一的结构, 即此处的JavaVM, 不同的虚拟机实现里, 真实的虚拟机对象可以完全不一样, 比如art虚拟机中的Runtime才是当之无愧的虚拟机。另外,一个Java进程只有一个JavaVM实例。在ART虚拟机中,JavaVM实际代表的是JavaVMExt类。
- JNIEnv代表JNI环境,每一个需要和Java交互(不管是Java层进入Native层, 还是Native层进入Java层)的线程都有一个独立的JNIEnv对象。同理,JNIEnv是JNI规范里指定的数据结构,不同虚拟机有不同的实现。在ART虚拟机中,JNIEnv实际代表的是JNIEnvExt类。
JavaVMExt
JavaVMExt的创建
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/runtime.cc
java_vm_ = new JavaVMExt(this, runtime_options);
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/java_vm_ext.h#39
class JavaVMExt : public JavaVM {
}
JavaVMExt 继承自 JavaVM ,是派生类。
JavaVM 是个_JavaVM结构体。
http://aospxref.com/android-7.1.2_r39/xref/libnativehelper/include/nativehelper/jni.h#141
struct _JavaVM;
...
typedef _JavaVM JavaVM;
来看看_JavaVM结构体的定义。
http://aospxref.com/android-7.1.2_r39/xref/libnativehelper/include/nativehelper/jni.h#1053
struct _JavaVM {
const struct JNIInvokeInterface* functions;
#if defined(__cplusplus)
jint DestroyJavaVM()
{ return functions->DestroyJavaVM(this); }
jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
{ return functions->AttachCurrentThread(this, p_env, thr_args); }
jint DetachCurrentThread()
{ return functions->DetachCurrentThread(this); }
jint GetEnv(void** env, jint version)
{ return functions->GetEnv(this, env, version); }
jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
{ return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
#endif /*__cplusplus*/
};
JNIInvokeInterface也是结构体。其中,JNIInvokeInterface的AttachCurrentThread、GetEnv等成员变量的数据类型都是函数指针。
而关于 宏 __cplusplus,具体可看这篇: 关于JNIEnv和JavaVM(C C++ 区别)
在C中:
使用JNIEnv* env要这样 (env)->方法名(env,参数列表)
使用JavaVM vm要这样 (vm)->方法名(vm,参数列表)
在C++中:
使用JNIEnv env要这样 env->方法名(参数列表)
使用JavaVM* vm要这样 vm->方法名(参数列表)
上面这二者的区别是,在C中必须先对env和vm间接寻址(得到的内容仍然是一个指针),在调用方法时要将env或vm传入作为第一个参数。C++则直接利用env和vm指针调用其成员。
JNIEnvExt
JNIEnvExt的思路和JavaVMExt类似。
http://aospxref.com/android-7.1.2_r39/xref/libnativehelper/include/nativehelper/jni.h#140
struct _JNIEnv;
...
typedef _JNIEnv JNIEnv;
...
struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;
#if defined(__cplusplus)
jint GetVersion()
{ return functions->GetVersion(this); }
jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }
jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }
...
#endif /*__cplusplus*/
};
http://aospxref.com/android-7.1.2_r39/xref/libnativehelper/include/nativehelper/jni.h
struct JNINativeInterface {
void* reserved0;
void* reserved1;
void* reserved2;
void* reserved3;
jint (*GetVersion)(JNIEnv *);
jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
jsize);
jclass (*FindClass)(JNIEnv*, const char*);
jmethodID (*FromReflectedMethod)(JNIEnv*, jobject);
jfieldID (*FromReflectedField)(JNIEnv*, jobject);
/* spec doesn't show jboolean parameter */
jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
...
}
现在来看JNIEnvExt ,是JNIEnv 的派生类。其创建是通过Create。
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/jni_env_ext.h#36
struct JNIEnvExt : public JNIEnv {
static JNIEnvExt* Create(Thread* self, JavaVMExt* vm);
}
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/jni_env_ext.cc
JNIEnvExt* JNIEnvExt::Create(Thread* self_in, JavaVMExt* vm_in) {
std::unique_ptr<JNIEnvExt> ret(new JNIEnvExt(self_in, vm_in));
if (CheckLocalsValid(ret.get())) {
return ret.release();
}
return nullptr;
}
其构造函数如下
http://aospxref.com/android-7.1.2_r39/xref/art/runtime/jni_env_ext.cc#JNIEnvExt
JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in)
: self(self_in),
vm(vm_in),
local_ref_cookie(IRT_FIRST_SEGMENT),
locals(kLocalsInitial, kLocalsMax, kLocal, false),
check_jni(false),
runtime_deleted(false),
critical(0),
monitors("monitors", kMonitorsInitial, kMonitorsMax) {
//GetJniNativeInterface 返回全局静态对象gJniNativeInterface
functions = unchecked_functions = GetJniNativeInterface();
if (vm->IsCheckJniEnabled()) {
SetCheckJniEnabled(true);
}
}
const JNINativeInterface* GetJniNativeInterface() {
return &gJniNativeInterface;
}
const JNINativeInterface gJniNativeInterface = {
nullptr, // reserved0.
nullptr, // reserved1.
nullptr, // reserved2.
nullptr, // reserved3.
JNI::GetVersion,
JNI::DefineClass,
JNI::FindClass,
...
}
这些函数为JNI类的静态成员变量,Java和JNI的函数
通过JNINativeInterface 关联起来。

JavaVM和JNIEnv这2个类的具体使用可参考这篇:
https://www.jianshu.com/p/d46ebdcfc3d9
之前简单整理了一下。
2.2.5 ClassLinker
在Java中,Class是最为重要的信息组织单元。所以,本节的主角 ClassLinker 也是ART中当仁不让的核心类。正如其类名所示 ——ClassLinker——类的连接器,即将类关联和管理起来。
Runtime Init 函数中和 ClassLinker 的关键代码,如下所示。
bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
...
// class_linker_ 是 runtime 的成员变量,其类型是 ClassLinker
class_linker_ = new ClassLinker(intern_table_);
// 根据boot镜像的内容初始化这个 ClassLinker 对象
bool result = class_linker_->InitFromBootImage(&error_msg);
...
}
ClassLinker 初始化流程暂时略过。
这里只关注一个知识点:
类是如何被加载的?
这里涉及到ClassLoader 相关知识点。
可参考这几篇文档:
Android类加载器ClassLoader
通俗易懂的双亲委派机制
类是如何被加载的?
类的执行过程

类的加载过程

1)加载、验证、准备、初始化这四个阶段的发生顺序与图中所描述的顺序相同,唯独解析阶段,它是不太固定的,它有可能发生在初始化之前,但是也有可能发生在初始化之后;
2)这几个阶段也并不是完全执行完某一个阶段,才可以发生下一个阶段,大部分情况下都是交叉混合进行的。
类加载器
JVM预定义了三种类加载器,分别是引导类加载器、拓展类加载器、应用类加载器

双亲委派机制工作原理:
1.如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行。
2.如果父类的加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终会到达顶层的启动类加载器。(从这里就可以看出来,类加载请求都会先到达启动类加载器)
3.如果父类加载器可以完成类加载任务,就成功返回,倘若无法完成此加载任务,则委派给它的子加载器去加载。如果有个类加载请求来了,会一直向上委托,直到引导类加载器;然后引导类加载器尝试加载,如果它不能加载,则会给他的子加载器扩展类加载器加载;如果扩展类加载器还是不能加载;则再到下一级系统类加载器。
双亲委派机制的优势
1.避免类的重复加载。一旦一个类被父类加载器加载之后,就不会再被委派给子类进行加载。
2.保护程序安全。
总结可看这张图


3. 总结
围绕 Runtime 对象的创建过程,介绍了 ART 虚拟机代码中一些关键模块、关键类和它们的功能。
- Runtime 对象是虚拟机的化身。整个虚拟机包含很多模块,这些模块对应的对象都可以通过 Runtime 相关接口获取。
- Thread 类代表虚拟机内部的执行线程,它和线程堆栈的设置、代码的执行等息息相关。
- Heap 类封装了加载到虚拟机进程里的各种内存映射对象,包括加载镜像文件的 ImageSpace ,用于分配内存的 MallocSpace 。Heap 还包含GC 相关很多功能。
- ClassLinker 用于管理虚拟机所加载的各种 Class 信息
- JavaVMExt 是 JavaVm 的派生类,是 ART 虚拟机 JNI 层中 Java 虚拟机的化身。还有一些其他辅助类,比如 MemMap、OatFileManager、GcRoot等。
- 最后,有一个很重要的知识点是 oat 和 art 的文件格式。虚拟机中的很多信息都是从 art 或 oat 文件里读取的。
参考链接:
类是如何被加载的?
Android类加载器ClassLoader
关于JNIEnv和JavaVM(C C++ 区别)
网友评论