-
Notifications
You must be signed in to change notification settings - Fork 0
Description
虚拟机的启动
在AndroidRuntime.start()函数中,系统调用了startVm函数,在startVm函数中,系统设置了启动dalvik虚拟机的参数,然后调用Jni.cpp#JNI_CreateJavaVM正式开始创建工作。我们的分析就是从这里开始。
1.首先将 DvmGlobals 结构清零,这个结构是存储一个进程中的dalvik虚拟机的信息。
struct JavaVMExt {
const struct JNIInvokeInterface* funcTable; /* must be first */
const struct JNIInvokeInterface* baseFuncTable;
/* head of list of JNIEnvs associated with this VM */
JNIEnvExt* envList;
pthread_mutex_t envListLock;
}; JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt)); //JavaVMExt
pVM->funcTable = &gInvokeInterface;
pVM->envList = NULL;static const struct JNIInvokeInterface gInvokeInterface = {
NULL,
NULL,
NULL,
DestroyJavaVM,
AttachCurrentThread,
DetachCurrentThread,
GetEnv,
AttachCurrentThreadAsDaemon,
};由代码可以知道,gInvokeInterface是一个函数跳转表,following:
struct JNIInvokeInterface {
void* reserved0;
void* reserved1;
void* reserved2;
jint (*DestroyJavaVM)(JavaVM*);
jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
jint (*DetachCurrentThread)(JavaVM*);
jint (*GetEnv)(JavaVM*, void**, jint);
jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
};每一个项都是一个指针。
2.接着开始检查每一个参数了。
3.实际上JavaVm结构就是一个跳转表JNIInvokeInterface,所以有
gDvmJni.jniVm = (JavaVM*) pVM; //JavaVMExt* pVMstruct _JavaVM {
const struct JNIInvokeInterface* functions;
};这里有一个强制类型转换。
为什么不直接让JNIEnvExt结构体从JNIEnv结构体继承下来呢?这样把一个JNIEnvExt结构体转换为一个JNIEnv结构体就是相当直观的。然而,Dalvik虚拟机的源代码并一定是要以C++语言的形式来编译的,它也可以以C语言的形式来编译的。由于C语言没有继承的概念,因此,为了使得Dalvik虚拟机的源代码能同时兼容C++和C,这里就使用了一个Trick:只要两个结构体的内存布局相同,它们就可以相互转换访问。当然,这并不要求两个结构体的内存布局完全相同,但是至少开始部分要求是相同的。在这种情况下,将一个结构体强制转换成另外一个结构体之外,只要不去访问内存布局不一致的地方,就没有问题。在Android系统的Native代码中,我们可以常常看到这种Trick。罗升阳博客
4.然后开始创建jni环境
JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);在dvmCreateJNIEnv(Thread self)中,创建一个
JNIEnvExt* newEnv; newEnv->funcTable = &gNativeInterface; struct JNIEnvExt {
const struct JNINativeInterface* funcTable; /* must be first */
const struct JNINativeInterface* baseFuncTable;
u4 envThreadId;
Thread* self;
/* if nonzero, we are in a "critical" JNI call */
int critical;
struct JNIEnvExt* prev;
struct JNIEnvExt* next;
};这里会构成一个双向链表,将新创建的env插入到gvmjni的jniVm中的列表中。 这里是android 4.4与老罗博客的2.3不一样,区别在于gvmjni从gvm中拆分出来了。
- 函数返回后就开始执行
dvmStartup(int argc, const char* const argv[],bool ignoreUnrecognized, JNIEnv* pEnv)这是关键函数。
std::string dvmStartup(int argc, const char* const argv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
{
setCommandLineDefaults(); //设置虚拟机默认参数
int cc = processOptions(argc, argv, ignoreUnrecognized);//处理参数
/*
* Initialize components.
*/
if (!dvmAllocTrackerStartup()) //对象分配记录子模块
if (!dvmGcStartup()) //gc
if (!dvmThreadStartup())
if (!dvmInlineNativeStartup()) //dalvik内联函数子模块。会替换一些java方法,效率更高。
if (!dvmRegisterMapStartup())
if (!dvmInstanceofStartup())
if (!dvmClassStartup())//来初始化启动类加载器(Bootstrap Class Loader),
//同时还会初始化java.lang.Class类。
//保证加载的Java核心类是合法的。
if (!dvmFindRequiredClassesAndMembers())
//这里,会对gdvm结构进行很多的填充操作。下面dalvik取得的systemloader也是在这里初始化的。
if (!dvmStringInternStartup())
if (!dvmNativeStartup()) //初始化SO库加载表,即创建了一张存储so文件目录的hash表
//并保存到gDvm.nativelibs中去
if (!dvmInternalNativeStartup())//用来初始化一个内部Native函数表,并保存到gDvm.userDexFiles
//所有需要直接访问Dalvik虚拟机内部函数或者数据结构的Native函数都定义在这张表中
//同样用hash表存储
if (!dvmJniStartup())//初始化全局引用表。
//被Native Code引用的Java对象就会被记录在一个全局引用表中
if (!dvmProfilingStartup())//Dalvik虚拟机的性能分析子模块
if (!dvmCreateInlineSubsTable())
if (!dvmValidateBoxClasses())//初始化装箱类
if (!dvmPrepMainForJni(pEnv))
if (!dvmInitClass(gDvm.classJavaLangClass))
if (!registerSystemNatives(pEnv))//会调用 loadJniLibrary加载libcore和nativehelper。进而进入
//dvmLoadNativeCode()
/*
1. 在gdvm的nativelibs(hash表)中查找so库是否已经被加载了,如果加载了就返回结果。
2. 没有被加载的情况下,会调用[addSharedLibEntry()](http://androidxref.com/4.4.2_r2/xref/dalvik/vm/Native.cpp#addSharedLibEntry)将so添加到gdvm.nativelibs。
3. 用dlopen打开需要被加载的库。如果该库没有JNI_ONLOAD,则直接返回成功。否则,由函数指针调用JNI_ONLOAD,最后会返回version,会判断version。
*/
if (!dvmCreateStockExceptions())
if (!dvmPrepMainThread())
/*
1. gDvm.classJavaLangClass || gDvm.classJavaLangThreadGroup) || gDvm.classJavaLangThread ||
gDvm.classJavaLangVMThread这几项
2. 反射调用创建Thread类实例,并调用main方法
3. VMThread,同2
4.取得gdvm的 systemLoader。上文所说的初始化,所以这里拿到的就是java/lang中的getsystemloader()方法返回的loader。
*/
if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)
//确保主线程当前不引用有任何Java对象,
if (!dvmDebuggerStartup())//用来初始化Dalvik虚拟机的调试环境
if (!dvmGcStartupClasses())
//会检查启动参数,判别是否是在zygote中启动的dalvik虚拟机
//对应于第一次创建:zygote中启动
//android app启动:dvmInitAfterZygote
if (gDvm.zygote) {
if (!initZygote()) {
return "initZygote failed";
}
} else {
if (!dvmInitAfterZygote()) {//在dvmInitAfterZygote()会开启JIT线程,如果选择JIT模式的话
return "dvmInitAfterZygote failed";
}
}
return "";
}总结一下,dalvik虚拟机的启动过程,就是在对gdvm结构的填充过程。
相关函数链接:
dvmLoadNativeCode()