关于Davlik解释器的一些思考

    一直以来对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);
    }
    ……
}


    6、我在看代码的时候发现并不是所有native代码都是通过jni机制,也就是类似于 env->CallStaticVoidMethod。也有直接调用dvmCallMethodV,我发现两处:

    之一在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启动应用程序的流程图,方便日后学习。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值