[讨论] [Dalvik VM] JIT部分与线程之间存在怎样的关系?

pzz2011 2014-11-03
在看Dalvik虚拟机的时候发现如下代码片段,所以想到了这个问题。
代码如下:
bool dvmCompilerStartup(void)
{

    dvmInitMutex(&gDvmJit.compilerLock);
    dvmInitMutex(&gDvmJit.compilerICPatchLock);
    dvmInitMutex(&gDvmJit.codeCacheProtectionLock);
    dvmLockMutex(&gDvmJit.compilerLock);
    pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
    pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);

    /* Reset the work queue */
    gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
    gDvmJit.compilerQueueLength = 0;
    dvmUnlockMutex(&gDvmJit.compilerLock);

    /*
     * Defer rest of initialization until we're sure JIT'ng makes sense. Launch
     * the compiler thread, which will do the real initialization if and
     * when it is signalled to do so.
     */
    return dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
                                   compilerThreadStart, NULL);
}


希望得到大家的帮助,在此表示感谢
RednaxelaFX 2014-11-04
楼主在读的是Kitkat-release版的代码么?觉得看起来像是。

Dalvik VM默认是在一个单独的线程上运行JIT编译器的。此时有且只有一个这样的线程。

Dalvik VM默认用混合模式执行:解释器与JIT编译器协作处理加载进来的Java程序。

程序先由解释器运行。所有线程、方法调用都共用一个解释器,具体的局部状态记录在每个线程的栈帧里。

解释器对若干Dalvik字节码有特殊处理,把它们看作trace可能开始的位置。如果在这种位置发现该字节码已经执行了足够次数,解释器就会在当前Java线程上发起一个build trace的请求(调用dvmJitCheckTraceRequest())。
这样,当前Java线程就会开始进入特殊的trace模式(kSubModeJitTraceBuild)来执行程序,在解释执行每条字节码之前先调用dvmCheckBefore()来完成trace动作,然后再正常解释这条字节码指令,一直到trace结束。
当trace到某些条件满足时,解释器回调用dvmJitEndTraceSelect()说明trace结束了。而这个函数会调用dvmCheckJit(),触发真正的JIT编译过程。这样会进一步来到dvmCompilerWorkEnqueue()函数,将刚刚收集到的trace加入到编译任务队列里。

另一边,Dalvik VM的JIT编译器会在VM启动过了一段时间之后再初始化。最迟在第一个JIT编译请求来的时候要初始化。回顾最初说的:这个JIT编译器在一个单独的线程上运行。
初始化之后JIT编译器就在它自己的线程上不断循环查看编译任务队列里有没有请求,一旦发现有请求就会去编译,然后把编译好的代码安装回到对应的方法上。

static void *compilerThreadStart(void *arg)
{
    // ...
    while (!gDvmJit.haltCompilerThread) {
        if (workQueueLength() == 0) {
            int cc;
            cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
            assert(cc == 0);
            pthread_cond_wait(&gDvmJit.compilerQueueActivity,
                              &gDvmJit.compilerLock);
            continue;
        } else {
            do {
                CompilerWorkOrder work = workDequeue();
                // 编译!!
                // ...
                        bool codeCompiled = dvmCompilerDoWork(&work);
                        dvmLockMutex(&gDvmJit.compilerLock);
                        if ((work.result.cacheVersion ==
                             gDvmJit.cacheVersion) &&
                             codeCompiled &&
                             !work.result.discardResult &&
                             work.result.codeAddress) {
                            // 安装编译结果
                            dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
                                              work.result.instructionSet,
                                              false, /* not method entry */
                                              work.result.profileCodeSize);
                // ...
            } while (workQueueLength() != 0);
        }
    }
  //...
}


默认情况下Dalvik应该是用异步编译的,就是说解释器在提交了编译任务到队列之后会继续解释执行,直到遇到某个字节码是一个已经编译好的trace的入口,那才跳到编译后的代码去。在Dalvik VM里这个异步行为由gDvmJit.blockingMode控制,true就是同步,false就是异步。
可以通过 -Xjitblocking 参数来指定不要异步模式。这样解释器在提交编译任务后会等到编译完成(dvmCompilerDrainQueue())才继续执行。
pzz2011 2014-11-04
恩  这里还有一个疑问,一般在执行一个程序的过程中,只有一个VM实例吧,那么这个VM实例是在一个虚拟机进程中,在这个进程下有多少个线程呢?
1. GC线程
2. JIT compiler线程
3. 解释器线程
...还有什么线程呢?Java语言层面上利用Thread类生成的线程和我以上提到的线程一样吗?还是有什么不同之处呀?

RednaxelaFX 写道
楼主在读的是Kitkat-release版的代码么?觉得看起来像是。

是啊 确实在看Kitkat-release,您也在看吗?

PS:R大人真好~~~

RednaxelaFX 2014-11-07
pzz2011 写道
RednaxelaFX 写道
楼主在读的是Kitkat-release版的代码么?觉得看起来像是。

是啊 确实在看Kitkat-release,您也在看吗?

我已经好久没看Dalvik代码了。看到您发帖问的问题才去瞄了眼代码而已。
Global site tag (gtag.js) - Google Analytics