/* * The garbage collection daemon. Initiates a concurrent collection * when signaled. Also periodically trims the heaps when a few seconds * have elapsed since the last concurrent GC. */ static void *gcDaemonThread(void* arg) { dvmChangeStatus(NULL, THREAD_VMWAIT); dvmLockMutex(&gHs->gcThreadMutex); while (gHs->gcThreadShutdown != true) { bool trim = false; if (gHs->gcThreadTrimNeeded) { int result = dvmRelativeCondWait(&gHs->gcThreadCond, &gHs->gcThreadMutex, HEAP_TRIM_IDLE_TIME_MS, 0); if (result == ETIMEDOUT) { /* Timed out waiting for a GC request, schedule a heap trim. */ trim = true; } } else { dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex); } // Many JDWP requests cause allocation. We can't take the heap lock and wait to // transition to runnable so we can start a GC if a debugger is connected, because // we don't know that the JDWP thread isn't about to allocate and require the // heap lock itself, leading to deadlock. http://b/8191824. if (gDvm.debuggerConnected) { continue; } dvmLockHeap(); /* * Another thread may have started a concurrent garbage * collection before we were scheduled. Check for this * condition before proceeding. */ if (!gDvm.gcHeap->gcRunning) { dvmChangeStatus(NULL, THREAD_RUNNING); if (trim) { trimHeaps(); gHs->gcThreadTrimNeeded = false; } else { dvmCollectGarbageInternal(GC_CONCURRENT); gHs->gcThreadTrimNeeded = true; } dvmChangeStatus(NULL, THREAD_VMWAIT); } dvmUnlockHeap(); } dvmChangeStatus(NULL, THREAD_RUNNING); return NULL; }
/* Block until the queue length is 0, or there is a pending suspend request */ void dvmCompilerDrainQueue(void) { Thread *self = dvmThreadSelf(); dvmLockMutex(&gDvmJit.compilerLock); while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread && self->suspendCount == 0) { /* * Use timed wait here - more than one mutator threads may be blocked * but the compiler thread will only signal once when the queue is * emptied. Furthermore, the compiler thread may have been shutdown * so the blocked thread may never get the wakeup signal. */ dvmRelativeCondWait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock, 1000, 0); } dvmUnlockMutex(&gDvmJit.compilerLock); }
/** * @brief 丢弃编译队列 */ void dvmCompilerDrainQueue(void) { Thread *self = dvmThreadSelf(); dvmLockMutex(&gDvmJit.compilerLock); /* 遍历整个队列,并不停的检测编译线程是否终止,并且自身线程没有被挂起 */ while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread && self->suspendCount == 0) { /* * Use timed wait here - more than one mutator threads may be blocked * but the compiler thread will only signal once when the queue is * emptied. Furthermore, the compiler thread may have been shutdown * so the blocked thread may never get the wakeup signal. */ /* 使用等待时间 - 超过一个mutator线程被阻塞但是编译器线程将 * 仅有一条一次当队列被提交,编译器线程不关闭,阻塞线程将永远不会唤醒*/ dvmRelativeCondWait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock, 1000, 0); } dvmUnlockMutex(&gDvmJit.compilerLock); }
/* * The garbage collection daemon. Initiates a concurrent collection * when signaled. Also periodically trims the heaps when a few seconds * have elapsed since the last concurrent GC. */ static void *gcDaemonThread(void* arg) { dvmChangeStatus(NULL, THREAD_VMWAIT); dvmLockMutex(&gHs->gcThreadMutex); while (gHs->gcThreadShutdown != true) { bool trim = false; if (gHs->gcThreadTrimNeeded) { int result = dvmRelativeCondWait(&gHs->gcThreadCond, &gHs->gcThreadMutex, HEAP_TRIM_IDLE_TIME_MS, 0); if (result == ETIMEDOUT) { /* Timed out waiting for a GC request, schedule a heap trim. */ trim = true; } } else { dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex); } dvmLockHeap(); /* * Another thread may have started a concurrent garbage * collection before we were scheduled. Check for this * condition before proceeding. */ if (!gDvm.gcHeap->gcRunning) { dvmChangeStatus(NULL, THREAD_RUNNING); if (trim) { trimHeaps(); gHs->gcThreadTrimNeeded = false; } else { dvmCollectGarbageInternal(GC_CONCURRENT); gHs->gcThreadTrimNeeded = true; } dvmChangeStatus(NULL, THREAD_VMWAIT); } dvmUnlockHeap(); } dvmChangeStatus(NULL, THREAD_RUNNING); return NULL; }
static void *compilerThreadStart(void *arg) { dvmChangeStatus(NULL, THREAD_VMWAIT); /* * If we're not running stand-alone, wait a little before * recieving translation requests on the assumption that process start * up code isn't worth compiling. We'll resume when the framework * signals us that the first screen draw has happened, or the timer * below expires (to catch daemons). * * There is a theoretical race between the callback to * VMRuntime.startJitCompiation and when the compiler thread reaches this * point. In case the callback happens earlier, in order not to permanently * hold the system_server (which is not using the timed wait) in * interpreter-only mode we bypass the delay here. */ if (gDvmJit.runningInAndroidFramework && !gDvmJit.alreadyEnabledViaFramework) { /* * If the current VM instance is the system server (detected by having * 0 in gDvm.systemServerPid), we will use the indefinite wait on the * conditional variable to determine whether to start the JIT or not. * If the system server detects that the whole system is booted in * safe mode, the conditional variable will never be signaled and the * system server will remain in the interpreter-only mode. All * subsequent apps will be started with the --enable-safemode flag * explicitly appended. */ if (gDvm.systemServerPid == 0) { dvmLockMutex(&gDvmJit.compilerLock); pthread_cond_wait(&gDvmJit.compilerQueueActivity, &gDvmJit.compilerLock); dvmUnlockMutex(&gDvmJit.compilerLock); ALOGD("JIT started for system_server"); } else { dvmLockMutex(&gDvmJit.compilerLock); /* * TUNING: experiment with the delay & perhaps make it * target-specific */ dvmRelativeCondWait(&gDvmJit.compilerQueueActivity, &gDvmJit.compilerLock, 3000, 0); dvmUnlockMutex(&gDvmJit.compilerLock); } if (gDvmJit.haltCompilerThread) { return NULL; } } compilerThreadStartup(); dvmLockMutex(&gDvmJit.compilerLock); /* * Since the compiler thread will not touch any objects on the heap once * being created, we just fake its state as VMWAIT so that it can be a * bit late when there is suspend request pending. */ 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(); dvmUnlockMutex(&gDvmJit.compilerLock); #if defined(WITH_JIT_TUNING) /* * This is live across setjmp(). Mark it volatile to suppress * a gcc warning. We should not need this since it is assigned * only once but gcc is not smart enough. */ volatile u8 startTime = dvmGetRelativeTimeUsec(); #endif /* * Check whether there is a suspend request on me. This * is necessary to allow a clean shutdown. * * However, in the blocking stress testing mode, let the * compiler thread continue doing compilations to unblock * other requesting threads. This may occasionally cause * shutdown from proceeding cleanly in the standalone invocation * of the vm but this should be acceptable. */ if (!gDvmJit.blockingMode) dvmCheckSuspendPending(dvmThreadSelf()); /* Is JitTable filling up? */ if (gDvmJit.jitTableEntriesUsed > (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) { bool resizeFail = dvmJitResizeJitTable(gDvmJit.jitTableSize * 2); /* * If the jit table is full, consider it's time to reset * the code cache too. */ gDvmJit.codeCacheFull |= resizeFail; } if (gDvmJit.haltCompilerThread) { ALOGD("Compiler shutdown in progress - discarding request"); } else if (!gDvmJit.codeCacheFull) { jmp_buf jmpBuf; work.bailPtr = &jmpBuf; bool aborted = setjmp(jmpBuf); if (!aborted) { bool codeCompiled = dvmCompilerDoWork(&work); /* * Make sure we are still operating with the * same translation cache version. See * Issue 4271784 for details. */ 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); } dvmUnlockMutex(&gDvmJit.compilerLock); } dvmCompilerArenaReset(); } free(work.info); #if defined(WITH_JIT_TUNING) gDvmJit.jitTime += dvmGetRelativeTimeUsec() - startTime; #endif dvmLockMutex(&gDvmJit.compilerLock); } while (workQueueLength() != 0); } } pthread_cond_signal(&gDvmJit.compilerQueueEmpty); dvmUnlockMutex(&gDvmJit.compilerLock); /* * As part of detaching the thread we need to call into Java code to update * the ThreadGroup, and we should not be in VMWAIT state while executing * interpreted code. */ dvmChangeStatus(NULL, THREAD_RUNNING); if (gDvm.verboseShutdown) ALOGD("Compiler thread shutting down"); return NULL; }