dalvik创建
所谓dalvik虚拟机就是在内存的一个单利对象gdvm 是一个DvmGlobals实例,用来收集当前进程所有虚拟机相关的信息,不过他掌管着所有对象的生成,线程管理,gc管理,资源的预加载。jni环境管理,异常管理,dex验证等等
DvmGlobals gDvm.vmList管理进程的虚拟机是 JavaVMExt实例
每一个Dalvik虚拟机实例还有一个JNI环境列表,保存在对应的JavaVMExt对象的成员变量envList中
JavaVMExt.envList 是一个JNIEnvExt双向,列表即JNI环境列表,其中,每一个Attach到Dalvik虚拟机中去的线程都有一个对应的JNIEnvExt,列表中的第一个JNIEnvExt对象描述的是主线程的JNI环境。
JNIEnvExt的funcTable 由全局变量gNativeInterface来描述
当我们需要在C/C++代码在中调用Java函数,就要用到这个本地接口表,例如:
- 调用函数FindClass可以找到指定的Java类;
- 调用函数GetMethodID可以获得一个Java类的成员函数,并且可以通过类似CallObjectMethod函数来间接调用它;
- 调用函数GetFieldID可以获得一个Java类的成员变量,并且可以通过类似SetIntField的函数来设置它的值;
- 调用函数RegisterNatives和UnregisterNatives可以注册和反注册JNI方法到一个Java类中去,以便可以在Java函数中调用;
- 调用函数GetJavaVM可以获得当前进程中的Dalvik虚拟机实例。
dalvik创建的时候会启动对象的生成记录,线程管理,gc管理,资源的预加载。jni环境管理,异常管理,dex验证等等
dalvik运行
Dalvik虚拟机在Zygote进程中启动完成之后,就会获得一个JavaVM实例和一个JNIEnv实例。其中,获得的JavaVM实例就是用来描述Zygote进程的Dalvik虚拟机实例,而获得的JNIEnv实例描述的是Zygote进程的主线程的JNI环境。紧接着,Zygote进程就会通过前面获得的JNIEnv实例的成员函数CallStaticVoidMethod来调用com.android.internal.os.ZygoteInit类的静态成员函数main。这就相当于是将com.android.internal.os.ZygoteInit类的静态成员函数main作为Java代码的入口点。
最终会调到 dvmInterpretStd和dvmMterpStd这两函数可以理解为dalvik的解释器入口,解释器是虚拟机的核心模块,它的性能关乎到整个虚拟机的性能。Dalvik虚拟机的解释器开始的时候都是以C语言来实现的,后来为了提到性能,就改成以汇编语言来实现。注意,无论Dalvik虚拟机的解释器是以C语言来实现,还是以汇编语言来实现,它们的源代码都是以一种模块化方法来自动生成的,并且能够根据某一个特定的平台进行优化。
主要执行方法
GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
u2 count, u2 regs)
{
STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
StackSaveArea* newSaveArea;
u4* newFp;
......
newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
newSaveArea = SAVEAREA_FROM_FP(newFp);
......
newSaveArea->prevFrame = fp;
newSaveArea->savedPc = pc;
......
if (!dvmIsNativeMethod(methodToCall)) {
/*
* "Call" interpreted code. Reposition the PC, update the
* frame pointer and other local state, and continue.
*/
curMethod = methodToCall;
methodClassDex = curMethod->clazz->pDvmDex;
pc = methodToCall->insns;
fp = self->curFrame = newFp;
......
FINISH(0); // jump to method start
} else {
/* set this up for JNI locals, even if not a JNI native */
......
self->curFrame = newFp;
......
/*
* Jump through native call bridge. Because we leave no
* space for locals on native calls, "newFp" points directly
* to the method arguments.
*/
(*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
......
if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
invokeInstr <= OP_INVOKE_INTERFACE*/)
{
FINISH(3);
}
......
}
......
}
GOTO_TARGET_END
分支invokeMethod首先是为当前要解释的成员函数methodToCall创建一个新的栈帧newFp,接着再通过调用函数dvmIsNativeMethod来判断成员函数methodToCall是否是一个JNI方法。
在新创建的栈帧newFp中,分别在其成员变量prevFrame和savedPc中保存了当前栈帧fp和当前程序计数器pc的值,这样使得在调用完成员函数methodToCall之后,可以返回到当前正在执行的成员函数的下一条指令中去,以及恢复当前线程的栈帧。
如果成员函数methodToCall不是一个JNI方法,那么就说明接下来仍然是要通过Dalvik虚拟机解释器来执行它。不过这时候需要将当前线程激活的栈帧fp设置为newFp,以及将程序计数器pc指向成员函数methodToCall的方法区。最后通过宏FINISH(0)来跳出当前分支,实际上就是跳出前面Step 6的switch语句,然后重复执行while循环语句。此时传递给宏FINISH的参数为0,表示不需要调整程序计数器pc的值,因为前面已经调整过了
以Zygote进程为例,Dalvik虚拟机解释器就是以com.android.internal.os.ZygoteInit类的静态成员函数main为入口点执行,然后在一个Socket上进行循环,用来等待和处理ActivityManagerService服务向它发送创建新应用程序进程的请求,直至系统退出为止。又以Android应用程序进程为例,Dalvik虚拟机解释器就是以android.app.ActivityThread类的静态成员函数main为入口点执行,然后在一消息队列上进行循环,用来等待和处理主线程的消息,直到应用程序退出为止。