一直以来对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。
如果native代码本身创建的对象,分配的空间是操作系统的堆空间,必须手动释放。
9、附上一张Laucher启动应用程序的流程图,方便日后学习。