安卓高手之路之ClassLoader(二) 安卓ClassLoader移动系统Dalvik
因为ClassLoader一定与虚拟机的启动有关系,那么必须从Zygote的启动开始看代码。下面就分析一下这些代码,行数不多:
int main(int argc, const char* const argv[]) { // These are global variables in ProcessState.cpp //ProcessState.cpp中可能要用到一些main函数。 mArgC = argc; mArgV = argv; mArgLen = 0; for (int i=0; i<argc; i++) { mArgLen += strlen(argv[i]) + 1; } mArgLen--; AppRuntime runtime; const char* argv0 = argv[0]; // Process command line arguments // ignore argv[0] argc--; argv++; // Everything up to '--' or first non '-' arg goes to the vm int i = runtime.addVmArguments(argc, argv); // Parse runtime arguments. Stop at first unrecognized option. bool zygote = false; bool startSystemServer = false; bool application = false; const char* parentDir = NULL; const char* niceName = NULL; const char* className = NULL; while (i < argc) { const char* arg = argv[i++]; if (!parentDir) { parentDir = arg; } else if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = "zygote"; } else if (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } else if (strcmp(arg, "--application") == 0) { application = true; } else if (strncmp(arg, "--nice-name=", 12) == 0) { niceName = arg + 12; } else { className = arg; break; } } if (niceName && *niceName) { setArgv0(argv0, niceName); set_process_name(niceName); } runtime.mParentDir = parentDir; if (zygote) { runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : ""); } else if (className) { // Remainder of args get passed to startup class main() runtime.mClassName = className; runtime.mArgC = argc - i; runtime.mArgV = argv + i; runtime.start("com.android.internal.os.RuntimeInit", application ? "application" : "tool"); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); return 10; } }
分析完之后发现如下参数规律:
1. argv[0]:用这个修改了进程名称。
2. 虚拟机参数:前面的选项参数都是以“-”打头。被放入了runtime。这些参数被称为是虚拟机参数。
3.“--”打头的参数是zygote参数。有如下几种,排列顺序如下:
-runtimearg[0]
-runtimearg[1]
。。。。
parentDir //这个也是runtime使用的,也就是VM使用的。
className//这个也是runtime使用的,也就是VM使用的。
--zygote
--start-system-server
--application
--nice-name=
然后,如果是zygote,那么进入下面这句话
runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : "");
如果有类名,那么进入下面这句话:
runtime.mClassName = className; runtime.mArgC = argc - i; //className,包括className以后的参数个数。 runtime.mArgV = argv + i; //截止到className的参数个数 runtime.start("com.android.internal.os.RuntimeInit", application ? "application" : "tool");
第一部分:那么开机第一次启动的就一定是,
runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : "");
其中startSystemServer 由init.rc指定,在目录android40\system\core\rootdir中的init.rc.
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
第二部分:从ActivityManagerService可以看出,--application并没有指定,这句话也就相当于:
runtime.start("com.android.internal.os.RuntimeInit", "tool");
现在代码分成了两部分。
那么先分析第一部分。
那么zygote启动到底配置了那些参数呢,我们就看一看:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
根据上面说的参数序列图,可以看出。
runtime.mParentDir 为/system/bin
runtime的一个arg为-Xzygote
那么这个这个start函数就变成:
runtime.start("com.android.internal.os.ZygoteInit", "start-system-server");
代码进入到base/core/jni目录的AndroidRuntime.cpp里面。这个函数还不算长,就直接贴出来看一下,注意注释,由此可以看出这个就是启动虚拟机的代码所在啊。那么既然Zygote进程也是这么启动的,那么我们就有理由断定Zygote也是个Dalvik虚拟机!事情是不是这样呢?那么就带着这个疑问去分析一下:
/* * Start the Android runtime. This involves starting the virtual machine * and calling the "static void main(String[] args)" method in the class * named by "className". * * Passes the main function two arguments, the class name and the specified * options string. */ void AndroidRuntime::start(const char* className, const char* options) { LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n", className != NULL ? className : "(unknown)"); blockSigpipe(); /* * 'startSystemServer == true' means runtime is obsolete and not run from * init.rc anymore, so we print out the boot start event here. */ if (strcmp(options, "start-system-server") == 0) { /* track our progress through the boot sequence */ const int LOG_BOOT_PROGRESS_START = 3000; LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); } const char* rootDir = getenv("ANDROID_ROOT"); if (rootDir == NULL) { rootDir = "/system"; if (!hasDir("/system")) { LOG_FATAL("No root directory specified, and /android does not exist."); return; } setenv("ANDROID_ROOT", rootDir, 1); } //const char* kernelHack = getenv("LD_ASSUME_KERNEL"); //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack); /* start the virtual machine */ JNIEnv* env; if (startVm(&mJavaVM, &env) != 0) { return; } onVmCreated(env); /* * Register android functions. */ if (startReg(env) < 0) { LOGE("Unable to register all android natives\n"); return; } /* * We want to call main() with a String array with arguments in it. * At present we have two arguments, the class name and an option string. * Create an array to hold them. */ jclass stringClass; jobjectArray strArray; jstring classNameStr; jstring optionsStr; stringClass = env->FindClass("java/lang/String"); assert(stringClass != NULL); strArray = env->NewObjectArray(2, stringClass, NULL); assert(strArray != NULL); classNameStr = env->NewStringUTF(className); assert(classNameStr != NULL); env->SetObjectArrayElement(strArray, 0, classNameStr); optionsStr = env->NewStringUTF(options); env->SetObjectArrayElement(strArray, 1, optionsStr); /* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */ char* slashClassName = toSlashClassName(className); jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { LOGE("JavaVM unable to locate class '%s'\n", slashClassName); /* keep going */ } else { jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V"); if (startMeth == NULL) { LOGE("JavaVM unable to find main() in '%s'\n", className); /* keep going */ } else { env->CallStaticVoidMethod(startClass, startMeth, strArray); #if 0 if (env->ExceptionCheck()) threadExitUncaughtException(env); #endif } } free(slashClassName); LOGD("Shutting down VM\n"); if (mJavaVM->DetachCurrentThread() != JNI_OK) LOGW("Warning: unable to detach main thread\n"); if (mJavaVM->DestroyJavaVM() != 0) LOGW("Warning: VM did not shut down cleanly\n"); }
linux的POSIX (Portable Operating System Interface of Unix)我不懂。但是从直观上看,可能是一种禁止打断进程的方法:
LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n", className != NULL ? className : "(unknown)"); blockSigpipe();
下面这句话毫无意义,就是打印log
if (strcmp(options, "start-system-server") == 0) { /* track our progress through the boot sequence */ const int LOG_BOOT_PROGRESS_START = 3000; LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); }
下面这句话定义androidroot的目录
const char* rootDir = getenv("ANDROID_ROOT"); if (rootDir == NULL) { rootDir = "/system"; if (!hasDir("/system")) { LOG_FATAL("No root directory specified, and /android does not exist."); return; } setenv("ANDROID_ROOT", rootDir, 1); }
对照init.rc可以知道,就是/system
# setup the global environment export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin export LD_LIBRARY_PATH /vendor/lib:/system/lib export ANDROID_BOOTLOGO 1 export ANDROID_ROOT /system export ANDROID_ASSETS /system/app export ANDROID_DATA /data export ASEC_MOUNTPOINT /mnt/asec export LOOP_MOUNTPOINT /mnt/obb export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
主要是下面这两句话
/* start the virtual machine */ JNIEnv* env; if (startVm(&mJavaVM, &env) != 0) { return; } onVmCreated(env); /* * Register android functions. */ if (startReg(env) < 0) { LOGE("Unable to register all android natives\n"); return; }
一个启动虚拟机,一个启动注册安卓本地方法。虚拟机的启动流程,最终调用的是
JNI_CreateJavaVM 在framework/base/core/jni/AndroidRuntime.cpp下。JNI_CreateJavaVM 调用的是:
然后调用dalvik/vm/Jni.cpp的JNI_CreateJavaVM 方法:
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args; if (args->version < JNI_VERSION_1_2) { return JNI_EVERSION; } // TODO: don't allow creation of multiple VMs -- one per customer for now /* zero globals; not strictly necessary the first time a VM is started */ memset(&gDvm, 0, sizeof(gDvm)); /* * Set up structures for JNIEnv and VM. */ JavaVMExt* pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt)); memset(pVM, 0, sizeof(JavaVMExt)); pVM->funcTable = &gInvokeInterface; pVM->envList = NULL; dvmInitMutex(&pVM->envListLock); UniquePtr<const char*[]> argv(new const char*[args->nOptions]); memset(argv.get(), 0, sizeof(char*) * (args->nOptions)); /* * Convert JNI args to argv. * * We have to pull out vfprintf/exit/abort, because they use the * "extraInfo" field to pass function pointer "hooks" in. We also * look for the -Xcheck:jni stuff here. */ int argc = 0; for (int i = 0; i < args->nOptions; i++) { const char* optStr = args->options[i].optionString; if (optStr == NULL) { dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i); return JNI_ERR; } else if (strcmp(optStr, "vfprintf") == 0) { gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo; } else if (strcmp(optStr, "exit") == 0) { gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo; } else if (strcmp(optStr, "abort") == 0) { gDvm.abortHook = (void (*)(void))args->options[i].extraInfo; } else if (strcmp(optStr, "sensitiveThread") == 0) { gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo; } else if (strcmp(optStr, "-Xcheck:jni") == 0) { gDvmJni.useCheckJni = true; } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) { char* jniOpts = strdup(optStr + 10); size_t jniOptCount = 1; for (char* p = jniOpts; *p != 0; ++p) { if (*p == ',') { ++jniOptCount; *p = 0; } } char* jniOpt = jniOpts; for (size_t i = 0; i < jniOptCount; ++i) { if (strcmp(jniOpt, "warnonly") == 0) { gDvmJni.warnOnly = true; } else if (strcmp(jniOpt, "forcecopy") == 0) { gDvmJni.forceCopy = true; } else if (strcmp(jniOpt, "logThirdPartyJni") == 0) { gDvmJni.logThirdPartyJni = true; } else { dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n", jniOpt); return JNI_ERR; } jniOpt += strlen(jniOpt) + 1; } free(jniOpts); } else { /* regular option */ argv[argc++] = optStr; } } if (gDvmJni.useCheckJni) { dvmUseCheckedJniVm(pVM); } if (gDvmJni.jniVm != NULL) { dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n"); return JNI_ERR; } gDvmJni.jniVm = (JavaVM*) pVM; /* * 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; std::string status = dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv); gDvm.initializing = false; if (!status.empty()) { free(pEnv); free(pVM); LOGW("CreateJavaVM failed: %s", status.c_str()); return JNI_ERR; } /* * Success! Return stuff to caller. */ dvmChangeStatus(NULL, THREAD_NATIVE); *p_env = (JNIEnv*) pEnv; *p_vm = (JavaVM*) pVM; LOGV("CreateJavaVM succeeded"); return JNI_OK; }
然后调用Jni.cpp中的
/* * Create a new JNIEnv struct and add it to the VM's list. * * "self" will be NULL for the main thread, since the VM hasn't started * yet; the value will be filled in later. */ JNIEnv* dvmCreateJNIEnv(Thread* self) { JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm; //if (self != NULL) // LOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self); assert(vm != NULL); JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt)); newEnv->funcTable = &gNativeInterface; if (self != NULL) { dvmSetJniEnvThreadId((JNIEnv*) newEnv, self); assert(newEnv->envThreadId != 0); } else { /* make it obvious if we fail to initialize these later */ newEnv->envThreadId = 0x77777775; newEnv->self = (Thread*) 0x77777779; } if (gDvmJni.useCheckJni) { dvmUseCheckedJniEnv(newEnv); } ScopedPthreadMutexLock lock(&vm->envListLock); /* insert at head of list */ newEnv->next = vm->envList; assert(newEnv->prev == NULL); if (vm->envList == NULL) { // rare, but possible vm->envList = newEnv; } else { vm->envList->prev = newEnv; } vm->envList = newEnv; //if (self != NULL) // LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self); return (JNIEnv*) newEnv; }
好吧,这些全是些乱七八糟的东西。真正启动的是这句话,Jni.cpp中:
std::string status = dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
在Dalvik/vm/Init.cpp中
* * VM initialization. Pass in any options provided on the command line. * Do not pass in the class name or the options for the class. * * Returns 0 on success. */ std::string dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized, JNIEnv* pEnv) { ScopedShutdown scopedShutdown; assert(gDvm.initializing); LOGV("VM init args (%d):", argc); for (int i = 0; i < argc; i++) { LOGV(" %d: '%s'", i, argv[i]); } setCommandLineDefaults(); /* * Process the option flags (if any). */ int cc = processOptions(argc, argv, ignoreUnrecognized); if (cc != 0) { if (cc < 0) { dvmFprintf(stderr, "\n"); usage("dalvikvm"); } return "syntax error"; } #if WITH_EXTRA_GC_CHECKS > 1 /* only "portable" interp has the extra goodies */ if (gDvm.executionMode != kExecutionModeInterpPortable) { LOGI("Switching to 'portable' interpreter for GC checks"); gDvm.executionMode = kExecutionModeInterpPortable; } #endif /* Configure group scheduling capabilities */ if (!access("/dev/cpuctl/tasks", F_OK)) { LOGV("Using kernel group scheduling"); gDvm.kernelGroupScheduling = 1; } else { LOGV("Using kernel scheduler policies"); } /* configure signal handling */ if (!gDvm.reduceSignals) blockSignals(); /* verify system page size */ if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) { return StringPrintf("expected page size %d, got %d", SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE)); } /* mterp setup */ LOGV("Using executionMode %d", gDvm.executionMode); dvmCheckAsmConstants(); /* * Initialize components. */ dvmQuasiAtomicsStartup(); if (!dvmAllocTrackerStartup()) { return "dvmAllocTrackerStartup failed"; } if (!dvmGcStartup()) { return "dvmGcStartup failed"; } if (!dvmThreadStartup()) { return "dvmThreadStartup failed"; } if (!dvmInlineNativeStartup()) { return "dvmInlineNativeStartup"; } if (!dvmRegisterMapStartup()) { return "dvmRegisterMapStartup failed"; } if (!dvmInstanceofStartup()) { return "dvmInstanceofStartup failed"; } if (!dvmClassStartup()) { return "dvmClassStartup failed"; } /* * At this point, the system is guaranteed to be sufficiently * initialized that we can look up classes and class members. This * call populates the gDvm instance with all the class and member * references that the VM wants to use directly. */ if (!dvmFindRequiredClassesAndMembers()) { return "dvmFindRequiredClassesAndMembers failed"; } if (!dvmStringInternStartup()) { return "dvmStringInternStartup failed"; } if (!dvmNativeStartup()) { return "dvmNativeStartup failed"; } if (!dvmInternalNativeStartup()) { return "dvmInternalNativeStartup failed"; } if (!dvmJniStartup()) { return "dvmJniStartup failed"; } if (!dvmProfilingStartup()) { return "dvmProfilingStartup failed"; } /* * Create a table of methods for which we will substitute an "inline" * version for performance. */ if (!dvmCreateInlineSubsTable()) { return "dvmCreateInlineSubsTable failed"; } /* * Miscellaneous class library validation. */ if (!dvmValidateBoxClasses()) { return "dvmValidateBoxClasses failed"; } /* * Do the last bits of Thread struct initialization we need to allow * JNI calls to work. */ if (!dvmPrepMainForJni(pEnv)) { return "dvmPrepMainForJni failed"; } /* * Explicitly initialize java.lang.Class. This doesn't happen * automatically because it's allocated specially (it's an instance * of itself). Must happen before registration of system natives, * which make some calls that throw assertions if the classes they * operate on aren't initialized. */ if (!dvmInitClass(gDvm.classJavaLangClass)) { return "couldn't initialized java.lang.Class"; } /* * Register the system native methods, which are registered through JNI. */ if (!registerSystemNatives(pEnv)) { return "couldn't register system natives"; } /* * Do some "late" initialization for the memory allocator. This may * allocate storage and initialize classes. */ if (!dvmCreateStockExceptions()) { return "dvmCreateStockExceptions failed"; } /* * At this point, the VM is in a pretty good state. Finish prep on * the main thread (specifically, create a java.lang.Thread object to go * along with our Thread struct). Note we will probably be executing * some interpreted class initializer code in here. */ if (!dvmPrepMainThread()) { return "dvmPrepMainThread failed"; } /* * Make sure we haven't accumulated any tracked references. The main * thread should be starting with a clean slate. */ if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0) { LOGW("Warning: tracked references remain post-initialization"); dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN"); } /* general debugging setup */ if (!dvmDebuggerStartup()) { return "dvmDebuggerStartup failed"; } if (!dvmGcStartupClasses()) { return "dvmGcStartupClasses failed"; } /* * Init for either zygote mode or non-zygote mode. The key difference * is that we don't start any additional threads in Zygote mode. */ if (gDvm.zygote) { if (!initZygote()) { return "initZygote failed"; } } else { if (!dvmInitAfterZygote()) { return "dvmInitAfterZygote failed"; } } #ifndef NDEBUG if (!dvmTestHash()) LOGE("dvmTestHash FAILED"); if (false /*noisy!*/ && !dvmTestIndirectRefTable()) LOGE("dvmTestIndirectRefTable FAILED"); #endif if (dvmCheckException(dvmThreadSelf())) { dvmLogExceptionStackTrace(); return "Exception pending at end of VM initialization"; } scopedShutdown.disarm(); return ""; }
代码真长。寻找其中最具价值的部分
插入代码:
if (!dvmAllocTrackerStartup()) { return "dvmAllocTrackerStartup failed"; } if (!dvmGcStartup()) { return "dvmGcStartup failed"; } if (!dvmThreadStartup()) { return "dvmThreadStartup failed"; } if (!dvmInlineNativeStartup()) { return "dvmInlineNativeStartup"; } if (!dvmRegisterMapStartup()) { return "dvmRegisterMapStartup failed"; } if (!dvmInstanceofStartup()) { return "dvmInstanceofStartup failed"; } if (!dvmClassStartup()) { return "dvmClassStartup failed"; }
经分析,这些都没有建立gc线程,gc线程的建立是在如下方法:
dvmInitAfterZygote
由于跟得太深,东西很多,就不一一列举。仅仅跟一下dvmClassStartup,最终调用到了dalvik/vm/oo/Class.cpp中的方法:
/* * Initialize the bootstrap class loader. * * Call this after the bootclasspath string has been finalized. */ bool dvmClassStartup() { /* make this a requirement -- don't currently support dirs in path */ if (strcmp(gDvm.bootClassPathStr, ".") == 0) { LOGE("ERROR: must specify non-'.' bootclasspath"); return false; } gDvm.loadedClasses = dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards); gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL); if (gDvm.pBootLoaderAlloc == NULL) return false; if (false) { linearAllocTests(); exit(0); } /* * Class serial number. We start with a high value to make it distinct * in binary dumps (e.g. hprof). */ gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER; /* * Set up the table we'll use for tracking initiating loaders for * early classes. * If it's NULL, we just fall back to the InitiatingLoaderList in the * ClassObject, so it's not fatal to fail this allocation. */ gDvm.initiatingLoaderList = (InitiatingLoaderList*) calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList)); /* * Create the initial classes. These are the first objects constructed * within the nascent VM. */ if (!createInitialClasses()) { return false; } /* * Process the bootstrap class path. This means opening the specified * DEX or Jar files and possibly running them through the optimizer. */ assert(gDvm.bootClassPath == NULL); processClassPath(gDvm.bootClassPathStr, true); if (gDvm.bootClassPath == NULL) return false; return true; }
根据注释,Initialize the bootstrap class loader.
这个函数告诉我们,他建立了boottrap classloader。
createInitialClasses加载了9大基本类型。而后的processClassPath则建立了基本的classloader。分析过后,比较失望。可能是为后续的boottrapclassloader做一些前期准备工作。
startVM就到这里。
好吧。现在又回到了最初的App_main.cpp中。进入了com.android.internal.os.ZygoteInit.java的main
public static void main(String argv[]) { try { // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); registerZygoteSocket(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preload(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); // Finish profiling the zygote initialization. SamplingProfilerIntegration.writeZygoteSnapshot(); // Do an initial gc to clean up after startup gc(); // If requested, start system server directly from Zygote if (argv.length != 2) { throw new RuntimeException(argv[0] + USAGE_STRING); } if (argv[1].equals("start-system-server")) { startSystemServer(); } else if (!argv[1].equals("")) { throw new RuntimeException(argv[0] + USAGE_STRING); } Log.i(TAG, "Accepting command socket connections"); if (ZYGOTE_FORK_MODE) { runForkMode(); } else { runSelectLoopMode(); } closeServerSocket(); } catch (MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { Log.e(TAG, "Zygote died with exception", ex); closeServerSocket(); throw ex; } }
回过头来继续看一下ZygoteInit.java这个类是如何初始化的,看如下代码:
/* * Create a new JNIEnv struct and add it to the VM's list. * * "self" will be NULL for the main thread, since the VM hasn't started * yet; the value will be filled in later. */ JNIEnv* dvmCreateJNIEnv(Thread* self) { JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm; //if (self != NULL) // LOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self); assert(vm != NULL); JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt)); newEnv->funcTable = &gNativeInterface; if (self != NULL) { dvmSetJniEnvThreadId((JNIEnv*) newEnv, self); assert(newEnv->envThreadId != 0); } else { /* make it obvious if we fail to initialize these later */ newEnv->envThreadId = 0x77777775; newEnv->self = (Thread*) 0x77777779; } if (gDvmJni.useCheckJni) { dvmUseCheckedJniEnv(newEnv); } ScopedPthreadMutexLock lock(&vm->envListLock); /* insert at head of list */ newEnv->next = vm->envList; assert(newEnv->prev == NULL); if (vm->envList == NULL) { // rare, but possible vm->envList = newEnv; } else { vm->envList->prev = newEnv; } vm->envList = newEnv; //if (self != NULL) // LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self); return (JNIEnv*) newEnv; }
最重要的是gNativeInterface 我们看定义,太长了,我们找到其中的FindClass。
static jclass FindClass(JNIEnv* env, const char* name) { ScopedJniThreadState ts(env); const Method* thisMethod = dvmGetCurrentJNIMethod(); assert(thisMethod != NULL); Object* loader; Object* trackedLoader = NULL; if (ts.self()->classLoaderOverride != NULL) { /* hack for JNI_OnLoad */ assert(strcmp(thisMethod->name, "nativeLoad") == 0); loader = ts.self()->classLoaderOverride; } else if (thisMethod == gDvm.methDalvikSystemNativeStart_main || thisMethod == gDvm.methDalvikSystemNativeStart_run) { /* start point of invocation interface */ if (!gDvm.initializing) { loader = trackedLoader = dvmGetSystemClassLoader(); } else { loader = NULL; } } else { loader = thisMethod->clazz->classLoader; } char* descriptor = dvmNameToDescriptor(name); if (descriptor == NULL) { return NULL; } ClassObject* clazz = dvmFindClassNoInit(descriptor, loader); free(descriptor); jclass jclazz = (jclass) addLocalReference(ts.self(), (Object*) clazz); dvmReleaseTrackedAlloc(trackedLoader, ts.self()); return jclazz; }
在dalvik/vm/Init.cpp中的方法对gVM的bootpath进行了初始化:
static void setCommandLineDefaults() { const char* envStr = getenv("CLASSPATH"); if (envStr != NULL) { gDvm.classPathStr = strdup(envStr); } else { gDvm.classPathStr = strdup("."); } envStr = getenv("BOOTCLASSPATH"); if (envStr != NULL) { gDvm.bootClassPathStr = strdup(envStr); } else { gDvm.bootClassPathStr = strdup("."); } gDvm.properties = new std::vector<std::string>(); /* Defaults overridden by -Xms and -Xmx. * TODO: base these on a system or application-specific default */ gDvm.heapStartingSize = 2 * 1024 * 1024; // Spec says 16MB; too big for us. gDvm.heapMaximumSize = 16 * 1024 * 1024; // Spec says 75% physical mem gDvm.heapGrowthLimit = 0; // 0 means no growth limit gDvm.stackSize = kDefaultStackSize; gDvm.concurrentMarkSweep = true; /* gDvm.jdwpSuspend = true; */
现在明白了,在init.rc中指定的BOOTCLASSPATH赋值给了gDvm.bootClassPathStr 。
而下面这个地方,则对FindClass进行了初始化。还是在dalvik/vm/Init.cpp中
*/ if (!dvmFindRequiredClassesAndMembers()) { return "dvmFindRequiredClassesAndMembers failed"; }
initDirectMethodReferences 把方法gDvm.methDalvikSystemNativeStart_main与NativeStart进行了对应。
{ &gDvm.methDalvikSystemNativeStart_main, "Ldalvik/system/NativeStart;", "main", "([Ljava/lang/String;)V" },
主要分析FindClass方法:
nativeLoad标示从System.loadlibrary加载。那么Zygote的dvmGetCurrentJNIMethod是哪个呢。我猜测,这个一定是没有的,也就是里面的成员变量全为空。哈哈。这意味着,最终调用到了
dvmFindClassNoInit方法中。
然后是ClassObject* dvmFindSystemClassNoInit(const char* descriptor)
{
return findClassNoInit(descriptor, NULL, NULL);
}
然后是findClassNoInit
ZygoteInit这个类根本是没有ClassLoader的。而是直接从包里面查找得到的。
进入ZygoteInit后。就是java代码了。