Dalvik VM启动流程之间单分析

3 篇文章 0 订阅

   Android system在Kernel初始化玩就会先启动initprocess, 而在init process之中会去启动Zygoteprocess, Zygote process是Android system中的一个程序产生器. 而在Androidsystem中每一道程序都会需要一个DVM用来执行. 所以Zygoteprocess在一开始就要先初始化Dalvik Virtual Machine. 在此针对DVM的初始阶段会分以下三点来分析.

        1. VM和JNIENV初始化

    2.  直译器初始流程

    3.  内存初始配置与管理.

 

VM和JNIENV初始化

    由程序代码来看, Dvm被启动时所会执行的事情, 可由mainfunction中看出如以下的流程:

\dalvik\dalvikvm\Main.cpp

1. 启动VM

/*
     * Start VM.  The current thread becomes the main thread of the VM.
     */
    if (JNI_CreateJavaVM(&vm, &env, &initArgs) < 0) {
        fprintf(stderr, "Dalvik VM init failed (check log file)\n");
        goto bail;
    }


2. 利用env的FindClassfunction来寻找程序的java class.

startClass = env->FindClass(slashClass);
    if (startClass == NULL) {
        fprintf(stderr, "Dalvik VM unable to locate class '%s'\n", slashClass);
        goto bail;
    }


3. 利用env的GetStaticMethodIDfunction搭配已找到的java class来取得mainfunction id.

startMeth = env->GetStaticMethodID(startClass,
                    "main", "([Ljava/lang/String;)V");
    if (startMeth == NULL) {
        fprintf(stderr, "Dalvik VM unable to find static main(String[]) in '%s'\n",
            slashClass);
        goto bail;
    }


4.  利用env 的CallStaticVoidMethodfunction搭配main function id 来呼叫javaclass的main function

/*
     * Invoke main().
     */
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
 
    if (!env->ExceptionCheck())
        result = 0;


        由上面的流程, 不难发现后面的动作都是需要由env的function去执行. 而env这个pointer到底是怎么来的? 答案就是第一步的JNI_CreateJavaVM执行获得的. 以下就是JNI_CreateJavaVM这个函数的流程.

 

JNI_CreateJavaVM

\dalvik\vm\Jni.cpp

1.  检查JNI version, 若比JNI_VERSION_1_2版本还小, 就直接回传JNI_EVERSION.

if (args->version < JNI_VERSION_1_2) {
        return JNI_EVERSION;
}


2.  初始化pVM并注册pVM一个functiontable gInvokeInterface. gInvokeInterface是一个有关VMlife cycle function table.

/*
     * Set up structures for JNIEnv and VM.
     */
    JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
    pVM->funcTable = &gInvokeInterface;
    pVM->envList = NULL;


3.   藉由dvmCreateJNIEnv函数来初始化pEnv并注册一个functiontable gNativeInterface, gNativeInterface是一个有关于nativejava通道的function table.

/*
     * Create a JNIEnv for the main thread.  We need to have something set up
     * here because some of the class initialization we do when starting
     * up the VM will call into native code.
     */
    JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
 
    /* Initialize VM. */
    gDvm.initializing = true;


4.  启动VM相关的初始功能, 比如gc,thread, class等等.

dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);


5   将初始化完成的pEnv和pVM指定给所传进来的参数p_env跟p_vm.

*p_env = (JNIEnv*) pEnv;
*p_vm = (JavaVM*) pVM;


        由第五步骤可以知道, 在mainfunction中所宣告的env的值就是由dvmCreateJNIEnv函数所产生的, 并且此env还带有个functiontable gNativeInterface. 因此我们就可以猜测在mainfunction中的第四个步骤env->CallStaticVoidMethod 的CallStaticVoidMethod函数必定在gNativeInterface function table中. 然而, 却找不到其实作. 这到底是怎么一回事. 以下就来一步一步的分析CallStaticVoidMethod函数的实作.

 

        在程序设计领域中, 一般若有太多不一样的接口拥有重复的动作. 在c的实作中, 通常会用一个macro(宏)来实作, 在c++的实作则用泛型的机制, 由于早期android版本的DVM是用c来实作, 因此在于不同接口同样动作的设计是采用c的宏设计. 依照这原理来看CallStaticVoidMethod这函数的实作, 只要在程序代码搜寻CallStatic相关字眼, 就不难找出其实作的地方.

CallStaticVoidMethod

\dalvik\vm\Jni.cpp

1.定义Static 函数

/*
 * Call a static method.
 */
#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref)               \
    static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz,    \
        jmethodID methodID, ...)                                   \
    {                                                               \
        UNUSED_PARAMETER(jclazz);                                \
        ScopedJniThreadState ts(env);                                \
        JValue result;                                             \
        va_list args;                                              \
        va_start(args, methodID);                                   \
        dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);                                                           \
        va_end(args);                                              \
        if (_isref && !dvmCheckException(ts.self()))                        \
            result.l = (Object*)addLocalReference(ts.self(), result.l);        \
        return _retok;                                            \
    }                                                          \                                      
    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz,   \
        jmethodID methodID, va_list args)                                  
    static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz,   \
        jmethodID methodID, jvalue* args)                                  
 
CALL_STATIC(jobject, Object, NULL, (jobject) result.l, true);
CALL_STATIC(jboolean, Boolean, 0, result.z, false);
CALL_STATIC(jbyte, Byte, 0, result.b, false);
CALL_STATIC(jchar, Char, 0, result.c, false);
CALL_STATIC(jshort, Short, 0, result.s, false);
CALL_STATIC(jint, Int, 0, result.i, false);
CALL_STATIC(jlong, Long, 0, result.j, false);
CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
CALL_STATIC(jdouble, Double, 0.0, result.d, false);
CALL_STATIC(void, Void, , , false);


由此程序代码可以看到, CALL_STATIC 宏定义了三个函数CallStatic##_jname##Method,CallStatic##_jname##MethodV, CallStatic##_jname##MethodA, 前面两个函数的差异在于函数是否有带型参. 最后一个函数是带有array参数的函数. 此三个函数中的CallStatic##_jname##Method,CallStatic##_jname##MethodV 会呼叫dvmCallMethodV 函数,CallStatic##_jname##MethodA则呼叫dvmCallMethodA 函数. 由于这里讨论的是CallStaticVoidMethod

这函数, 因此只会继续针对dvmCallMethodV函数继续分析下去, 对于dvmCallMethodA 函数的实作就不在这里做分析.

 

dvmCallMethodV

\dalvik\vm\interp\Stack.cpp

1. 将Callframe 推入堆栈中, 并取得目前Framepointer: self->interpSave.curFrame

clazz = callPrep(self, method, obj, false);


 

2.判断是否是NativeMethod, 在Android设计中, 所谓的NativeMathod就是一种 Virtual Method, 也就是说此Method只是一种单存的接口, 其实作需要看所指向的数据型态, 算是面向对象中的泛型机制.

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, pResult,
                              method, self);
        TRACE_METHOD_EXIT(self, method);
    } else {
        dvmInterpret(self, method, pResult);
    }


        由上面的程序代码可以看到, 若不是NativeMethod就直接呼叫dvmInterpret函数作Interpreter的动作.

3. 从堆栈中弹出Callframe. 表示目前的函数指令已执行完毕.

dvmPopFrame(self);


        由与这里只是简单的介绍一些初始化流程, 因此对于dvmCallMethodV的函数分析也只是点到为止, 里面对于指令的解析, 执行动作就留待之后在研究.

 

直译器初始流程

        DVM直译器的分析先前有篇文章 Dalvik interpreter 笔记 已经有分析过里面的流程运作了, 这里的重点会在于Interpreper初始化的准备工作流程.

 

dvmInterpret

dalvik\vm\interp\Interp.cpp

1. 先将目前的interpreter状态作备份.

/*
     * Save interpreter state from previous activation, linking
     * new to last.
*/
    interpSaveState = self->interpSave;


2. 开始注册新的interpreter状态.

self->interpSave.method = method;
self->interpSave.curFrame = (u4*) self->interpSave.curFrame;
self->interpSave.pc = method->insns;


3. 针对interpreter的设定来选用interpreter的型态, 在 Dalvik interpreter 笔记 中有介绍到Android的interpreter有分两类, fast 跟portable. 这两类的分析在 Dalvik interpreter 笔记  中有记录.Android 预设的interpreter是 fastinterpreter.

typedef void (*Interpreter)(Thread*);
    Interpreter stdInterp;
    if (gDvm.executionMode == kExecutionModeInterpFast)
        stdInterp = dvmMterpStd;
#if defined(WITH_JIT)
    else if (gDvm.executionMode == kExecutionModeJit ||
             gDvm.executionMode == kExecutionModeNcgO0 ||
             gDvm.executionMode == kExecutionModeNcgO1)
        stdInterp = dvmMterpStd;
#endif
    else
        stdInterp = dvmInterpretPortable;


4.  呼叫interpreter 开始执行直译动作.

// Call the interpreter
    (*stdInterp)(self);


5.  所有的指令直译完毕之后, 就回存先前备份的interpreter状态.

/* Restore interpreter state from previous activation */
    self->interpSave = interpSaveState;


 

内存初始配置与管理

        Android在DVM的内存管理设计, 在之前的 Android_DVM_MemoryManagement_研究分析 已经有详细的做研究分析了, 在这里会着重在DVM如何初始化内存以及为之后的GC机制作准备的流程. 在这篇一开始的时候有分析到一个dvmStartup函数, 这个函数中用来初始化DVM所需要的一些功能,

        dvmAllocTrackerStartup

        dvmGcStartup

        dvmThreadStartup

        dvmInlineNativeStartup

        dvmRegisterMapStartup

        dvmInstanceofStartup

        dvmClassStartup

        [...]

其中 dvmGcStartup 函数就是用来初始化GC的动作.

 

dvmGcStartup

\dalvik\vm\alloc\Alloc.cpp

1. 初始化gcHeapLock锁, 之后呼叫dvmHeapStartup函数开始进入初始化流程.

dvmInitMutex(&gDvm.gcHeapLock);
pthread_cond_init(&gDvm.gcHeapCond, NULL);
return dvmHeapStartup();


 

dvmHeapStartup

\dalvik\vm\alloc\Heap.cpp

1. 指定所配置的Heapmemory总共size的大小, 在android的默认值是16MB.

if (gDvm.heapGrowthLimit == 0) {
        gDvm.heapGrowthLimit = gDvm.heapMaximumSize;
    }


2. 初始化GC所需要的HeapSource, Android在这里的Heap Source是用来管理整个系统占用内存的一套系统, GC便能借由HeapSource来做回收机制. 至于HeapSource的产生跟管理流程在 Android_DVM_MemoryManagement_研究分析 有作一些研究心得.在这就不在分析了.

gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize,
                                  gDvm.heapMaximumSize,
                                  gDvm.heapGrowthLimit);


3.  将以分配好的Heap source指定给gDvm.gcHeap为后续动作做准备.

  [...]
    gDvm.gcHeap = gcHeap;


4.  设定一个list来记录需要回收的"参考"对象.

/* Set up the lists we'll use for cleared reference objects.
*/
    gcHeap->clearedReferences = NULL;
 
    if (!dvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit))    
   {
        LOGE_HEAP("card table startup failed.");
        return false;
    }


由以上的程序代码可以发现GC初始化到最后会呼叫dvmCardTableStartup函数来制作一份CardTable, 最主要是要来记录GC要回收的数据. 如何使用这个Cardtable来做回收机制, GC在作dvmCollectGarbageInternal的流程中作循环scanmarked 对象会用到, 详请可自行研究dvmHeapReScanMarkedObjects 函数.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值