一直以来对Davlik虚拟机都有一些疑问:
1、最初被davlik解释执行的java代码是什么呢?
2、dalvik解释执行java代码时,如果遇到native代码(C/C++)怎么办呢?
3、如果native代码想调用java代码,davlik是怎么解释执行的呢?
4、Zygote进程fork出应用程序进程后,davlik是怎么解释执行ActivityThread类的main函数呢?
5、davlik虚拟机是怎么解释执行java反射代码的呢?
6、native代码想要调用java代码,只能通过jni机制来让davlik解释执行java代码么?
7、davlik虚拟机三种加载类的方式是什么?
8、davlik虚拟机解释执行java代码new instance时,本质发生了什么?
回答:
1、最初被davlik解释执行的java代码是com.android.internal.os.ZygoteInit类的静态成员函数main,是通过env->CallStaticVoidMethod(startClass, startMeth, strArray); 这种JNI机制来解释执行的。最后会调用到dvmCallMethodV,如下:
void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
bool fromJni, JValue* pResult, va_list args)
{
......
if (dvmIsNativeMethod(method)) {
TRACE_METHOD_ENTER(self, method);
/*
* Because we leave no space for local variables, "curFrame" points
* directly at the method arguments.
*/
(*method->nativeFunc)(self->curFrame, pResult, method, self);
TRACE_METHOD_EXIT(self, method);
} else {
dvmInterpret(self, method, pResult);
}
......
}
2、答案在dalvik/vm/mterp/out/InterpC-portable.cpp代码中:
GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
u2 count, u2 regs)
{
……
if (!dvmIsNativeMethod(methodToCall)) {
/*
* "Call" interpreted code. Reposition the PC, update the
* frame pointer and other local state, and continue.
*/
curMethod = methodToCall;
self->interpSave.method = curMethod;
methodClassDex = curMethod->clazz->pDvmDex;
pc = methodToCall->insns;
fp = newFp;
self->interpSave.curFrame = fp;
#ifdef EASY_GDB
debugSaveArea = SAVEAREA_FROM_FP(newFp);
#endif
self->debugIsMethodEntry = true; // profiling, debugging
ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
curMethod->name, curMethod->shorty);
DUMP_REGS(curMethod, fp, true); // show input args
FINISH(0); // jump to method start
} else {
/* set this up for JNI locals, even if not a JNI native */
newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
self->interpSave.curFrame = newFp;
DUMP_REGS(methodToCall, newFp, true); // show input args
if (self->interpBreak.ctl.subMode != 0) {
dvmReportPreNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
}
ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
methodToCall->name, methodToCall->shorty);
/*
* 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 (self->interpBreak.ctl.subMode != 0) {
dvmReportPostNativeInvoke(methodToCall, self, newSaveArea->prevFrame);
}
/* pop frame off */
dvmPopJniLocals(self, newSaveArea);
self->interpSave.curFrame = newSaveArea->prevFrame;
fp = newSaveArea->prevFrame;
…….
}
}
assert(false); // should not get here
GOTO_TARGET_END
解释执行的过程中,如果是native方法,那么直接调用nativeFunc去执行。如果不是native方法,那么调到对应method的pc处去解释执行。
3、参考1问题的回答,是一个env的环境变量调用的,例:env->CallStaticVoidMethod(startClass, startMeth, strArray);最后调用到dvmCallMethodV,如下:
void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
bool fromJni, JValue* pResult, va_list args)
{
......
if (dvmIsNativeMethod(method)) {
TRACE_METHOD_ENTER(self, method);
/*
* Because we leave no space for local variables, "curFrame" points
* directly at the method arguments.
*/
(*method->nativeFunc)(self->curFrame, pResult, method, self);
TRACE_METHOD_EXIT(self, method);
} else {
dvmInterpret(self, method, pResult);
}
......
}
4、Zygote进程fork出应用程序进程时,此时应用程序调用函数的堆栈也和Zygote进程现在的堆栈一样,Android采用的一种抛异常清理堆栈的方法来执行ActivityThread类的main函数。
5、先看一下执行ActivityThread类的main函数的代码,就是通过反射机制。
public class ZygoteInit {
......
public static class MethodAndArgsCaller extends Exception
implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });//调用了com.android.server.SystemServer的main函数
} catch (IllegalAccessException ex) {
......
} catch (InvocationTargetException ex) {
......
}
}
}
......
}
dvmInvokeMethod代码如下:
Object* dvmInvokeMethod(Object* obj, const Method* method,
ArrayObject* argList, ArrayObject* params, ClassObject* returnType,
bool noAccessCheck)
{
……
if (dvmIsNativeMethod(method)) {
TRACE_METHOD_ENTER(self, method);
/*
* Because we leave no space for local variables, "curFrame" points * directly at the method arguments.
*/
(*method->nativeFunc)((u4*)self->interpSave.curFrame, &retval,
method, self);
TRACE_METHOD_EXIT(self, method);
} else {
dvmInterpret(self, method, &retval);
}
……
}
之一在findClassFromLoaderNoInit方法中:
const Method* loadClass =
loader->clazz->vtable[gDvm.voffJavaLangClassLoader_loadClass];
JValue result;
dvmCallMethod(self, loadClass, loader, &result, nameObj);
此时要解释执行的java代码是ClassLoader.loadClass。
还一处是创建了davlik线程后davlik来解释执行run方法,参考Dalvik虚拟机进程和线程的创建过程分析。
dvmCallMethod(self, run, self->threadObj, &unused);
dvmCallMethod调用了dvmCallMethodV。
7、三种方式,参考下图:
显式加载:
(1)ClassLoader.loadClass对应Dalvik_dalvik_system_DexFile_defineClassNative
(2)Class.forName对应Dalvik_java_lang_Class_classForName
隐式加载:
(3)对应dvmResolveClass
第三种方式什么时候调用呢?
在davlik虚拟机解释执行到GOTO_TARGET(invokeVirtual, bool methodCallRange, bool),会执行如下方法:
baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
if (baseMethod == NULL) {
baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
if (baseMethod == NULL) {
ILOGV("+ unknown method or access denied");
GOTO_exceptionThrown();
}
}
首先通过dvmDexGetResovedMethod来获取本类对象的method,此时classobject已经生成,所以这里使用方法名是
ResolvedMethod。
如果该类还没有被加载,那么就要调用dvmResolveMethod首先加载类,然后找到对应的方法,具体实现如下:
Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx,
MethodType methodType)
{
DvmDex* pDvmDex = referrer->pDvmDex;
ClassObject* resClass;
const DexMethodId* pMethodId;
Method* resMethod;
assert(methodType != METHOD_INTERFACE);
LOGVV("--- resolving method %u (referrer=%s)", methodIdx,
referrer->descriptor);
pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
resClass = dvmResolveClass(referrer, pMethodId->classIdx, false);
if (methodType == METHOD_DIRECT) {
resMethod = dvmFindDirectMethod(resClass, name, &proto);
} else if (methodType == METHOD_STATIC) {
resMethod = dvmFindDirectMethodHier(resClass, name, &proto);
} else {
resMethod = dvmFindVirtualMethodHier(resClass, name, &proto);
}
……
}
在这个方法中调用如上图中的dvmResolveClass。
8、无论是通过jni机制,例:env->NewObjectArray(2, stringClass, NULL),还是dalvik解释执行到new instance,本质上都是davlik虚拟机的堆上分配对象,可以垃圾回收。
如果native代码本身创建的对象,分配的空间是操作系统的堆空间,必须手动释放。
9、附上一张Laucher启动应用程序的流程图,方便日后学习。