安卓高手之路之ClassLoader(二)
因为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中
- */
- f (!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代码了。
安卓高手之路之ClassLoader(二)的更多相关文章
- 安卓高手之路之 ClassLoader
我不喜欢那些泛泛而谈的去讲那些形而上学的道理,更不喜欢记那些既定的东西.靠记忆去弥补思考的人,容易陷入人云亦云的境地,最后必定被记忆所围困,而最终消亡的是创造力.希望这个高手之路系列能够记录我学习安卓 ...
- 安卓高手之路之ClassLoader(三)
由于看C++和C代码看得很累,很辛苦.上一章终于解脱到java代码中来了. 第一个getClassLoader发生在main的preload方法中, public static void main(S ...
- ClassLoader使用记录《安卓高手之路》
我不喜欢那些泛泛而谈的去讲那些形而上学的道理,更不喜欢记那些既定的东西.靠记忆去弥补思考的人,容易陷入人云亦云的境地,最后必定被记忆所围困,而最终消亡的是创造力.希望这个高手之路系列能够记录我学习安卓 ...
- 安卓高手之路之 WindowManager
安卓中的画面不是纯粹由window组成.而是改成了window+view的组织模式.window是一个顶层窗口的概念.view就相当于在window内的控件.而subwindow则是依附于window ...
- 安卓高手之路之PackageManagerservice
源码位置:frameworks/base/core/java/android/content/pm/PackageParser.java 源文件路径:android\frameworks\base\s ...
- 安卓高手之路之java层Binder
很多人一提到Binder就说代理模式,人云亦云的多,能理解精髓的少. 本篇文章就从设计角度分析一下java层BInder的设计目标,以及设计思路,设计缺陷,从而驾驭它. 对于[邦德儿]的理解, 从通信 ...
- Python高手之路【十二】面向对象设计模式
单例模式 单例,顾名思义单个实例. class Person: __instance = None def __init__(self): pass @staticmethod def getInst ...
- [js高手之路] html5 canvas系列教程 - arcTo(弧度与二次,三次贝塞尔曲线以及在线工具)
之前,我写了一个arc函数的用法:[js高手之路] html5 canvas系列教程 - arc绘制曲线图形(曲线,弧线,圆形). arcTo: cxt.arcTo( cx, cy, x2, y2, ...
- [js高手之路]从原型链开始图解继承到组合继承的产生
基于javascript原型链的层层递进查找规则,以及原型对象(prototype)的共享特性,实现继承是非常简单的事情 一.把父类的实例对象赋给子类的原型对象(prototype),可以实现继承 f ...
随机推荐
- wifidog 源码初分析(3)-转
上一篇分析了 接入设备 在接入路由器,并发起首次 HTTP/80 请求到路由器上时,wifidog 是如何将此 HTTP 请求重定向至 auth-server 的流程. 之后 接入设备 的浏览器接收到 ...
- 恶性循环中的永生bug,可以说是相当写实了
恶性循环中的永生bug,可以说是相当写实了
- Android 设计原则【转载+整理】
原文地址 本文内容 吸引我的眼球 简化我的生活 让我眼前一亮 在使用过大量 Android APP 后,你会发现,遵循了下面这些原则的 APP 将会有更好的用户体验. 我们知道,往往国企的那些软件,都 ...
- android中使用spinner组件
spinner组件类似于html中的select标签,实现下拉选择框的功能. 添加一个Activity,界面上添加一个spinner下拉框,一个button按钮.点击按钮,获取spinner下拉框当前 ...
- ionic3中创建pipe管道
1.使用ionic cli创建pipe管道文件 ionic g pipe parse-date 该命令会在src文件夹创建pipes/parse-date/parse-date.ts文件,并且会在pi ...
- WinForm 之 VS2010发布、打包安装程序
第一步.在vs2010 打开要打包的应用程序解决方案,右键“ 解决方案 ” → “ 添加 ” → “ 新建项目 ” → “ 其他项目类型 ” → “ 安装和部署 ” → “ Visual Studio ...
- 微信小程序 - wxpage
WXPAGE 开源地址如下:https://github.com/tvfe/wxpage 极快的小程序打开 - 势必是用户体验的重中之重 #页面描述 A:代表全局App.js var wxpage = ...
- Java ThreadLocal (Java代码实战-006)
ThreadLocal解决什么问题 由于 ThreadLocal 支持范型,如 ThreadLocal< StringBuilder >,为表述方便,后文用 变量 代表 ThreadLoc ...
- java 生成jar包并保留注释
java 生成jar包并保留注释 CreationTime--2018年7月17日08点32分 Author:Marydon 1.选中java项目-->右键-->Export: 2.去 ...
- 〖Linux〗Kubuntu14.04 平滑字体的设置
有没有感觉终端的字体锯齿感觉非常强? 经过搜索后发现可以平滑字体显示得更漂亮一点: System Settings > Application Appearance > Fonts I e ...