因为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代码了。
相关推荐
用于验证理解Android中Classloader加载类机制的程序demo,从中可以对比DexClassLoader和PathClassLoader的区别联系。
Java 虚拟机中ClassLoader 相关简介 双亲委托机制 Android 中ClassLoader 简介
自定义classloader的使用
ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的ClassLoader运行机制 自己写的
ClassLoader原理,ClassLoader原理 ClassLoader原理
Java ClassLoader定制实例
这篇文章主要讲类加载器在android中如何动态的加载其他工程类的过程,对于类加载器的知识就跳过了。
破解java加密的ClassLoader.java,在classloader植入破解代码
Classloader
java classloader classpath 张孝祥
理解Java ClassLoader机制
重温java之classloader体系结构(含hotswap) 启动类加载器 扩展类加载器 系统类加载器
classloader 源码,自定义classloader
ClassLoader类加载机制和原理详解
自定义ClassLoader,控制台输入调试。 运行期间 重新载入指定目录的class文件。可实现对于类的功能函数更新。 用到java 反射,@interface 等技术
JVM内存模型,类加载模式工作机制详细,内存屏障,类从被加载到虚拟机内存中开始,直到卸载出内存为止,它的整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载这7个阶段。其中,验证、准备和解析这三...
关于J2EE服务器的ClassLoader的原理,该文档清晰了揭示了jvm装载类的顺序,同时用户可以自定义修改classLoader的配置 通过该文档,可以加深对Java虚拟机的理解
JVM ClassLoader简析.压缩包中文档和示例代码