安卓高手之路之ClassLoader(二)

因为ClassLoader一定与虚拟机的启动有关系,那么必须从Zygote的启动开始看代码。下面就分析一下这些代码,行数不多:

Cpp代码  

  1. int main(int argc, const char* const argv[])
  2. {
  3. // These are global variables in ProcessState.cpp
  4. //ProcessState.cpp中可能要用到一些main函数。
  5. mArgC = argc;
  6. mArgV = argv;
  7. mArgLen = 0;
  8. for (int i=0; i<argc; i++) {
  9. mArgLen += strlen(argv[i]) + 1;
  10. }
  11. mArgLen--;
  12. AppRuntime runtime;
  13. const char* argv0 = argv[0];
  14. // Process command line arguments
  15. // ignore argv[0]
  16. argc--;
  17. argv++;
  18. // Everything up to ‘--‘ or first non ‘-‘ arg goes to the vm
  19. int i = runtime.addVmArguments(argc, argv);
  20. // Parse runtime arguments.  Stop at first unrecognized option.
  21. bool zygote = false;
  22. bool startSystemServer = false;
  23. bool application = false;
  24. const char* parentDir = NULL;
  25. const char* niceName = NULL;
  26. const char* className = NULL;
  27. while (i < argc) {
  28. const char* arg = argv[i++];
  29. if (!parentDir) {
  30. parentDir = arg;
  31. } else if (strcmp(arg, "--zygote") == 0) {
  32. zygote = true;
  33. niceName = "zygote";
  34. } else if (strcmp(arg, "--start-system-server") == 0) {
  35. startSystemServer = true;
  36. } else if (strcmp(arg, "--application") == 0) {
  37. application = true;
  38. } else if (strncmp(arg, "--nice-name=", 12) == 0) {
  39. niceName = arg + 12;
  40. } else {
  41. className = arg;
  42. break;
  43. }
  44. }
  45. if (niceName && *niceName) {
  46. setArgv0(argv0, niceName);
  47. set_process_name(niceName);
  48. }
  49. runtime.mParentDir = parentDir;
  50. if (zygote) {
  51. runtime.start("com.android.internal.os.ZygoteInit",
  52. startSystemServer ? "start-system-server" : "");
  53. } else if (className) {
  54. // Remainder of args get passed to startup class main()
  55. runtime.mClassName = className;
  56. runtime.mArgC = argc - i;
  57. runtime.mArgV = argv + i;
  58. runtime.start("com.android.internal.os.RuntimeInit",
  59. application ? "application" : "tool");
  60. } else {
  61. fprintf(stderr, "Error: no class name or --zygote supplied.\n");
  62. app_usage();
  63. LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
  64. return 10;
  65. }
  66. }

分析完之后发现如下参数规律:

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,那么进入下面这句话

C代码  

  1. runtime.start("com.android.internal.os.ZygoteInit",
  2. startSystemServer ? "start-system-server" : "");

如果有类名,那么进入下面这句话:

C代码  

  1. runtime.mClassName = className;
  2. runtime.mArgC = argc - i; //className,包括className以后的参数个数。
  3. runtime.mArgV = argv + i; //截止到className的参数个数
  4. runtime.start("com.android.internal.os.RuntimeInit",
  5. application ? "application" : "tool");

第一部分:那么开机第一次启动的就一定是,

Java代码  

  1. 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并没有指定,这句话也就相当于:

C代码  

  1. 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函数就变成:

C代码  

  1. runtime.start("com.android.internal.os.ZygoteInit",  "start-system-server");

代码进入到base/core/jni目录的AndroidRuntime.cpp里面。这个函数还不算长,就直接贴出来看一下,注意注释,由此可以看出这个就是启动虚拟机的代码所在啊。那么既然Zygote进程也是这么启动的,那么我们就有理由断定Zygote也是个Dalvik虚拟机!事情是不是这样呢?那么就带着这个疑问去分析一下:

Cpp代码  

  1. /*
  2. * Start the Android runtime.  This involves starting the virtual machine
  3. * and calling the "static void main(String[] args)" method in the class
  4. * named by "className".
  5. *
  6. * Passes the main function two arguments, the class name and the specified
  7. * options string.
  8. */
  9. void AndroidRuntime::start(const char* className, const char* options)
  10. {
  11. LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
  12. className != NULL ? className : "(unknown)");
  13. blockSigpipe();
  14. /*
  15. * ‘startSystemServer == true‘ means runtime is obsolete and not run from
  16. * init.rc anymore, so we print out the boot start event here.
  17. */
  18. if (strcmp(options, "start-system-server") == 0) {
  19. /* track our progress through the boot sequence */
  20. const int LOG_BOOT_PROGRESS_START = 3000;
  21. LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
  22. ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
  23. }
  24. const char* rootDir = getenv("ANDROID_ROOT");
  25. if (rootDir == NULL) {
  26. rootDir = "/system";
  27. if (!hasDir("/system")) {
  28. LOG_FATAL("No root directory specified, and /android does not exist.");
  29. return;
  30. }
  31. setenv("ANDROID_ROOT", rootDir, 1);
  32. }
  33. //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
  34. //LOGD("Found LD_ASSUME_KERNEL=‘%s‘\n", kernelHack);
  35. /* start the virtual machine */
  36. JNIEnv* env;
  37. if (startVm(&mJavaVM, &env) != 0) {
  38. return;
  39. }
  40. onVmCreated(env);
  41. /*
  42. * Register android functions.
  43. */
  44. if (startReg(env) < 0) {
  45. LOGE("Unable to register all android natives\n");
  46. return;
  47. }
  48. /*
  49. * We want to call main() with a String array with arguments in it.
  50. * At present we have two arguments, the class name and an option string.
  51. * Create an array to hold them.
  52. */
  53. jclass stringClass;
  54. jobjectArray strArray;
  55. jstring classNameStr;
  56. jstring optionsStr;
  57. stringClass = env->FindClass("java/lang/String");
  58. assert(stringClass != NULL);
  59. strArray = env->NewObjectArray(2, stringClass, NULL);
  60. assert(strArray != NULL);
  61. classNameStr = env->NewStringUTF(className);
  62. assert(classNameStr != NULL);
  63. env->SetObjectArrayElement(strArray, 0, classNameStr);
  64. optionsStr = env->NewStringUTF(options);
  65. env->SetObjectArrayElement(strArray, 1, optionsStr);
  66. /*
  67. * Start VM.  This thread becomes the main thread of the VM, and will
  68. * not return until the VM exits.
  69. */
  70. char* slashClassName = toSlashClassName(className);
  71. jclass startClass = env->FindClass(slashClassName);
  72. if (startClass == NULL) {
  73. LOGE("JavaVM unable to locate class ‘%s‘\n", slashClassName);
  74. /* keep going */
  75. } else {
  76. jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
  77. "([Ljava/lang/String;)V");
  78. if (startMeth == NULL) {
  79. LOGE("JavaVM unable to find main() in ‘%s‘\n", className);
  80. /* keep going */
  81. } else {
  82. env->CallStaticVoidMethod(startClass, startMeth, strArray);
  83. #if 0
  84. if (env->ExceptionCheck())
  85. threadExitUncaughtException(env);
  86. #endif
  87. }
  88. }
  89. free(slashClassName);
  90. LOGD("Shutting down VM\n");
  91. if (mJavaVM->DetachCurrentThread() != JNI_OK)
  92. LOGW("Warning: unable to detach main thread\n");
  93. if (mJavaVM->DestroyJavaVM() != 0)
  94. LOGW("Warning: VM did not shut down cleanly\n");
  95. }

linux的POSIX (Portable Operating System Interface of Unix)我不懂。但是从直观上看,可能是一种禁止打断进程的方法:

Cpp代码  

  1. LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
  2. className != NULL ? className : "(unknown)");
  3. blockSigpipe();

下面这句话毫无意义,就是打印log

Cpp代码  

  1. if (strcmp(options, "start-system-server") == 0) {
  2. /* track our progress through the boot sequence */
  3. const int LOG_BOOT_PROGRESS_START = 3000;
  4. LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
  5. ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
  6. }

下面这句话定义androidroot的目录

Java代码  

  1. const char* rootDir = getenv("ANDROID_ROOT");
  2. if (rootDir == NULL) {
  3. rootDir = "/system";
  4. if (!hasDir("/system")) {
  5. LOG_FATAL("No root directory specified, and /android does not exist.");
  6. return;
  7. }
  8. setenv("ANDROID_ROOT", rootDir, 1);
  9. }

对照init.rc可以知道,就是/system

Python代码  

  1. # setup the global environment
  2. export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
  3. export LD_LIBRARY_PATH /vendor/lib:/system/lib
  4. export ANDROID_BOOTLOGO 1
  5. export ANDROID_ROOT /system
  6. export ANDROID_ASSETS /system/app
  7. export ANDROID_DATA /data
  8. export ASEC_MOUNTPOINT /mnt/asec
  9. export LOOP_MOUNTPOINT /mnt/obb
  10. 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

主要是下面这两句话

Java代码  

  1. /* start the virtual machine */
  2. JNIEnv* env;
  3. if (startVm(&mJavaVM, &env) != 0) {
  4. return;
  5. }
  6. onVmCreated(env);
  7. /*
  8. * Register android functions.
  9. */
  10. if (startReg(env) < 0) {
  11. LOGE("Unable to register all android natives\n");
  12. return;
  13. }

一个启动虚拟机,一个启动注册安卓本地方法。虚拟机的启动流程,最终调用的是

JNI_CreateJavaVM 在framework/base/core/jni/AndroidRuntime.cpp下。JNI_CreateJavaVM 调用的是:

然后调用dalvik/vm/Jni.cpp的JNI_CreateJavaVM 方法:

Cpp代码  

  1. jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  2. const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
  3. if (args->version < JNI_VERSION_1_2) {
  4. return JNI_EVERSION;
  5. }
  6. // TODO: don‘t allow creation of multiple VMs -- one per customer for now
  7. /* zero globals; not strictly necessary the first time a VM is started */
  8. memset(&gDvm, 0, sizeof(gDvm));
  9. /*
  10. * Set up structures for JNIEnv and VM.
  11. */
  12. JavaVMExt* pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
  13. memset(pVM, 0, sizeof(JavaVMExt));
  14. pVM->funcTable = &gInvokeInterface;
  15. pVM->envList = NULL;
  16. dvmInitMutex(&pVM->envListLock);
  17. UniquePtr<const char*[]> argv(new const char*[args->nOptions]);
  18. memset(argv.get(), 0, sizeof(char*) * (args->nOptions));
  19. /*
  20. * Convert JNI args to argv.
  21. *
  22. * We have to pull out vfprintf/exit/abort, because they use the
  23. * "extraInfo" field to pass function pointer "hooks" in.  We also
  24. * look for the -Xcheck:jni stuff here.
  25. */
  26. int argc = 0;
  27. for (int i = 0; i < args->nOptions; i++) {
  28. const char* optStr = args->options[i].optionString;
  29. if (optStr == NULL) {
  30. dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i);
  31. return JNI_ERR;
  32. } else if (strcmp(optStr, "vfprintf") == 0) {
  33. gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;
  34. } else if (strcmp(optStr, "exit") == 0) {
  35. gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;
  36. } else if (strcmp(optStr, "abort") == 0) {
  37. gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;
  38. } else if (strcmp(optStr, "sensitiveThread") == 0) {
  39. gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;
  40. } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
  41. gDvmJni.useCheckJni = true;
  42. } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
  43. char* jniOpts = strdup(optStr + 10);
  44. size_t jniOptCount = 1;
  45. for (char* p = jniOpts; *p != 0; ++p) {
  46. if (*p == ‘,‘) {
  47. ++jniOptCount;
  48. *p = 0;
  49. }
  50. }
  51. char* jniOpt = jniOpts;
  52. for (size_t i = 0; i < jniOptCount; ++i) {
  53. if (strcmp(jniOpt, "warnonly") == 0) {
  54. gDvmJni.warnOnly = true;
  55. } else if (strcmp(jniOpt, "forcecopy") == 0) {
  56. gDvmJni.forceCopy = true;
  57. } else if (strcmp(jniOpt, "logThirdPartyJni") == 0) {
  58. gDvmJni.logThirdPartyJni = true;
  59. } else {
  60. dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option ‘%s‘\n",
  61. jniOpt);
  62. return JNI_ERR;
  63. }
  64. jniOpt += strlen(jniOpt) + 1;
  65. }
  66. free(jniOpts);
  67. } else {
  68. /* regular option */
  69. argv[argc++] = optStr;
  70. }
  71. }
  72. if (gDvmJni.useCheckJni) {
  73. dvmUseCheckedJniVm(pVM);
  74. }
  75. if (gDvmJni.jniVm != NULL) {
  76. dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");
  77. return JNI_ERR;
  78. }
  79. gDvmJni.jniVm = (JavaVM*) pVM;
  80. /*
  81. * Create a JNIEnv for the main thread.  We need to have something set up
  82. * here because some of the class initialization we do when starting
  83. * up the VM will call into native code.
  84. */
  85. JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
  86. /* Initialize VM. */
  87. gDvm.initializing = true;
  88. std::string status =
  89. dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
  90. gDvm.initializing = false;
  91. if (!status.empty()) {
  92. free(pEnv);
  93. free(pVM);
  94. LOGW("CreateJavaVM failed: %s", status.c_str());
  95. return JNI_ERR;
  96. }
  97. /*
  98. * Success!  Return stuff to caller.
  99. */
  100. dvmChangeStatus(NULL, THREAD_NATIVE);
  101. *p_env = (JNIEnv*) pEnv;
  102. *p_vm = (JavaVM*) pVM;
  103. LOGV("CreateJavaVM succeeded");
  104. return JNI_OK;
  105. }

然后调用Jni.cpp中的

Java代码  

  1. /*
  2. * Create a new JNIEnv struct and add it to the VM‘s list.
  3. *
  4. * "self" will be NULL for the main thread, since the VM hasn‘t started
  5. * yet; the value will be filled in later.
  6. */
  7. JNIEnv* dvmCreateJNIEnv(Thread* self) {
  8. JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
  9. //if (self != NULL)
  10. //    LOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);
  11. assert(vm != NULL);
  12. JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
  13. newEnv->funcTable = &gNativeInterface;
  14. if (self != NULL) {
  15. dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
  16. assert(newEnv->envThreadId != 0);
  17. } else {
  18. /* make it obvious if we fail to initialize these later */
  19. newEnv->envThreadId = 0x77777775;
  20. newEnv->self = (Thread*) 0x77777779;
  21. }
  22. if (gDvmJni.useCheckJni) {
  23. dvmUseCheckedJniEnv(newEnv);
  24. }
  25. ScopedPthreadMutexLock lock(&vm->envListLock);
  26. /* insert at head of list */
  27. newEnv->next = vm->envList;
  28. assert(newEnv->prev == NULL);
  29. if (vm->envList == NULL) {
  30. // rare, but possible
  31. vm->envList = newEnv;
  32. } else {
  33. vm->envList->prev = newEnv;
  34. }
  35. vm->envList = newEnv;
  36. //if (self != NULL)
  37. //    LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
  38. return (JNIEnv*) newEnv;
  39. }

好吧,这些全是些乱七八糟的东西。真正启动的是这句话,Jni.cpp中:

Java代码  

  1. std::string status =
  2. dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);

在Dalvik/vm/Init.cpp中

Cpp代码  

  1. *
  2. * VM initialization.  Pass in any options provided on the command line.
  3. * Do not pass in the class name or the options for the class.
  4. *
  5. * Returns 0 on success.
  6. */
  7. std::string dvmStartup(int argc, const char* const argv[],
  8. bool ignoreUnrecognized, JNIEnv* pEnv)
  9. {
  10. ScopedShutdown scopedShutdown;
  11. assert(gDvm.initializing);
  12. LOGV("VM init args (%d):", argc);
  13. for (int i = 0; i < argc; i++) {
  14. LOGV("  %d: ‘%s‘", i, argv[i]);
  15. }
  16. setCommandLineDefaults();
  17. /*
  18. * Process the option flags (if any).
  19. */
  20. int cc = processOptions(argc, argv, ignoreUnrecognized);
  21. if (cc != 0) {
  22. if (cc < 0) {
  23. dvmFprintf(stderr, "\n");
  24. usage("dalvikvm");
  25. }
  26. return "syntax error";
  27. }
  28. #if WITH_EXTRA_GC_CHECKS > 1
  29. /* only "portable" interp has the extra goodies */
  30. if (gDvm.executionMode != kExecutionModeInterpPortable) {
  31. LOGI("Switching to ‘portable‘ interpreter for GC checks");
  32. gDvm.executionMode = kExecutionModeInterpPortable;
  33. }
  34. #endif
  35. /* Configure group scheduling capabilities */
  36. if (!access("/dev/cpuctl/tasks", F_OK)) {
  37. LOGV("Using kernel group scheduling");
  38. gDvm.kernelGroupScheduling = 1;
  39. } else {
  40. LOGV("Using kernel scheduler policies");
  41. }
  42. /* configure signal handling */
  43. if (!gDvm.reduceSignals)
  44. blockSignals();
  45. /* verify system page size */
  46. if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
  47. return StringPrintf("expected page size %d, got %d",
  48. SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
  49. }
  50. /* mterp setup */
  51. LOGV("Using executionMode %d", gDvm.executionMode);
  52. dvmCheckAsmConstants();
  53. /*
  54. * Initialize components.
  55. */
  56. dvmQuasiAtomicsStartup();
  57. if (!dvmAllocTrackerStartup()) {
  58. return "dvmAllocTrackerStartup failed";
  59. }
  60. if (!dvmGcStartup()) {
  61. return "dvmGcStartup failed";
  62. }
  63. if (!dvmThreadStartup()) {
  64. return "dvmThreadStartup failed";
  65. }
  66. if (!dvmInlineNativeStartup()) {
  67. return "dvmInlineNativeStartup";
  68. }
  69. if (!dvmRegisterMapStartup()) {
  70. return "dvmRegisterMapStartup failed";
  71. }
  72. if (!dvmInstanceofStartup()) {
  73. return "dvmInstanceofStartup failed";
  74. }
  75. if (!dvmClassStartup()) {
  76. return "dvmClassStartup failed";
  77. }
  78. /*
  79. * At this point, the system is guaranteed to be sufficiently
  80. * initialized that we can look up classes and class members. This
  81. * call populates the gDvm instance with all the class and member
  82. * references that the VM wants to use directly.
  83. */
  84. if (!dvmFindRequiredClassesAndMembers()) {
  85. return "dvmFindRequiredClassesAndMembers failed";
  86. }
  87. if (!dvmStringInternStartup()) {
  88. return "dvmStringInternStartup failed";
  89. }
  90. if (!dvmNativeStartup()) {
  91. return "dvmNativeStartup failed";
  92. }
  93. if (!dvmInternalNativeStartup()) {
  94. return "dvmInternalNativeStartup failed";
  95. }
  96. if (!dvmJniStartup()) {
  97. return "dvmJniStartup failed";
  98. }
  99. if (!dvmProfilingStartup()) {
  100. return "dvmProfilingStartup failed";
  101. }
  102. /*
  103. * Create a table of methods for which we will substitute an "inline"
  104. * version for performance.
  105. */
  106. if (!dvmCreateInlineSubsTable()) {
  107. return "dvmCreateInlineSubsTable failed";
  108. }
  109. /*
  110. * Miscellaneous class library validation.
  111. */
  112. if (!dvmValidateBoxClasses()) {
  113. return "dvmValidateBoxClasses failed";
  114. }
  115. /*
  116. * Do the last bits of Thread struct initialization we need to allow
  117. * JNI calls to work.
  118. */
  119. if (!dvmPrepMainForJni(pEnv)) {
  120. return "dvmPrepMainForJni failed";
  121. }
  122. /*
  123. * Explicitly initialize java.lang.Class.  This doesn‘t happen
  124. * automatically because it‘s allocated specially (it‘s an instance
  125. * of itself).  Must happen before registration of system natives,
  126. * which make some calls that throw assertions if the classes they
  127. * operate on aren‘t initialized.
  128. */
  129. if (!dvmInitClass(gDvm.classJavaLangClass)) {
  130. return "couldn‘t initialized java.lang.Class";
  131. }
  132. /*
  133. * Register the system native methods, which are registered through JNI.
  134. */
  135. if (!registerSystemNatives(pEnv)) {
  136. return "couldn‘t register system natives";
  137. }
  138. /*
  139. * Do some "late" initialization for the memory allocator.  This may
  140. * allocate storage and initialize classes.
  141. */
  142. if (!dvmCreateStockExceptions()) {
  143. return "dvmCreateStockExceptions failed";
  144. }
  145. /*
  146. * At this point, the VM is in a pretty good state.  Finish prep on
  147. * the main thread (specifically, create a java.lang.Thread object to go
  148. * along with our Thread struct).  Note we will probably be executing
  149. * some interpreted class initializer code in here.
  150. */
  151. if (!dvmPrepMainThread()) {
  152. return "dvmPrepMainThread failed";
  153. }
  154. /*
  155. * Make sure we haven‘t accumulated any tracked references.  The main
  156. * thread should be starting with a clean slate.
  157. */
  158. if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)
  159. {
  160. LOGW("Warning: tracked references remain post-initialization");
  161. dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
  162. }
  163. /* general debugging setup */
  164. if (!dvmDebuggerStartup()) {
  165. return "dvmDebuggerStartup failed";
  166. }
  167. if (!dvmGcStartupClasses()) {
  168. return "dvmGcStartupClasses failed";
  169. }
  170. /*
  171. * Init for either zygote mode or non-zygote mode.  The key difference
  172. * is that we don‘t start any additional threads in Zygote mode.
  173. */
  174. if (gDvm.zygote) {
  175. if (!initZygote()) {
  176. return "initZygote failed";
  177. }
  178. } else {
  179. if (!dvmInitAfterZygote()) {
  180. return "dvmInitAfterZygote failed";
  181. }
  182. }
  183. #ifndef NDEBUG
  184. if (!dvmTestHash())
  185. LOGE("dvmTestHash FAILED");
  186. if (false /*noisy!*/ && !dvmTestIndirectRefTable())
  187. LOGE("dvmTestIndirectRefTable FAILED");
  188. #endif
  189. if (dvmCheckException(dvmThreadSelf())) {
  190. dvmLogExceptionStackTrace();
  191. return "Exception pending at end of VM initialization";
  192. }
  193. scopedShutdown.disarm();
  194. return "";
  195. }

代码真长。寻找其中最具价值的部分

插入代码:

Java代码  

  1. if (!dvmAllocTrackerStartup()) {
  2. return "dvmAllocTrackerStartup failed";
  3. }
  4. if (!dvmGcStartup()) {
  5. return "dvmGcStartup failed";
  6. }
  7. if (!dvmThreadStartup()) {
  8. return "dvmThreadStartup failed";
  9. }
  10. if (!dvmInlineNativeStartup()) {
  11. return "dvmInlineNativeStartup";
  12. }
  13. if (!dvmRegisterMapStartup()) {
  14. return "dvmRegisterMapStartup failed";
  15. }
  16. if (!dvmInstanceofStartup()) {
  17. return "dvmInstanceofStartup failed";
  18. }
  19. if (!dvmClassStartup()) {
  20. return "dvmClassStartup failed";
  21. }

经分析,这些都没有建立gc线程,gc线程的建立是在如下方法:

Java代码  

  1. dvmInitAfterZygote

由于跟得太深,东西很多,就不一一列举。仅仅跟一下dvmClassStartup,最终调用到了dalvik/vm/oo/Class.cpp中的方法:

Java代码  

  1. /*
  2. * Initialize the bootstrap class loader.
  3. *
  4. * Call this after the bootclasspath string has been finalized.
  5. */
  6. bool dvmClassStartup()
  7. {
  8. /* make this a requirement -- don‘t currently support dirs in path */
  9. if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
  10. LOGE("ERROR: must specify non-‘.‘ bootclasspath");
  11. return false;
  12. }
  13. gDvm.loadedClasses =
  14. dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
  15. gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
  16. if (gDvm.pBootLoaderAlloc == NULL)
  17. return false;
  18. if (false) {
  19. linearAllocTests();
  20. exit(0);
  21. }
  22. /*
  23. * Class serial number.  We start with a high value to make it distinct
  24. * in binary dumps (e.g. hprof).
  25. */
  26. gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
  27. /*
  28. * Set up the table we‘ll use for tracking initiating loaders for
  29. * early classes.
  30. * If it‘s NULL, we just fall back to the InitiatingLoaderList in the
  31. * ClassObject, so it‘s not fatal to fail this allocation.
  32. */
  33. gDvm.initiatingLoaderList = (InitiatingLoaderList*)
  34. calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
  35. /*
  36. * Create the initial classes. These are the first objects constructed
  37. * within the nascent VM.
  38. */
  39. if (!createInitialClasses()) {
  40. return false;
  41. }
  42. /*
  43. * Process the bootstrap class path.  This means opening the specified
  44. * DEX or Jar files and possibly running them through the optimizer.
  45. */
  46. assert(gDvm.bootClassPath == NULL);
  47. processClassPath(gDvm.bootClassPathStr, true);
  48. if (gDvm.bootClassPath == NULL)
  49. return false;
  50. return true;
  51. }

根据注释,Initialize the bootstrap class loader.
这个函数告诉我们,他建立了boottrap classloader。

createInitialClasses加载了9大基本类型。而后的processClassPath则建立了基本的classloader。分析过后,比较失望。可能是为后续的boottrapclassloader做一些前期准备工作。

startVM就到这里。

好吧。现在又回到了最初的App_main.cpp中。进入了com.android.internal.os.ZygoteInit.java的main

Java代码  

  1. public static void main(String argv[]) {
  2. try {
  3. // Start profiling the zygote initialization.
  4. SamplingProfilerIntegration.start();
  5. registerZygoteSocket();
  6. EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
  7. SystemClock.uptimeMillis());
  8. preload();
  9. EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
  10. SystemClock.uptimeMillis());
  11. // Finish profiling the zygote initialization.
  12. SamplingProfilerIntegration.writeZygoteSnapshot();
  13. // Do an initial gc to clean up after startup
  14. gc();
  15. // If requested, start system server directly from Zygote
  16. if (argv.length != 2) {
  17. throw new RuntimeException(argv[0] + USAGE_STRING);
  18. }
  19. if (argv[1].equals("start-system-server")) {
  20. startSystemServer();
  21. } else if (!argv[1].equals("")) {
  22. throw new RuntimeException(argv[0] + USAGE_STRING);
  23. }
  24. Log.i(TAG, "Accepting command socket connections");
  25. if (ZYGOTE_FORK_MODE) {
  26. runForkMode();
  27. } else {
  28. runSelectLoopMode();
  29. }
  30. closeServerSocket();
  31. } catch (MethodAndArgsCaller caller) {
  32. caller.run();
  33. } catch (RuntimeException ex) {
  34. Log.e(TAG, "Zygote died with exception", ex);
  35. closeServerSocket();
  36. throw ex;
  37. }
  38. }

回过头来继续看一下ZygoteInit.java这个类是如何初始化的,看如下代码:

Java代码  

  1. /*
  2. * Create a new JNIEnv struct and add it to the VM‘s list.
  3. *
  4. * "self" will be NULL for the main thread, since the VM hasn‘t started
  5. * yet; the value will be filled in later.
  6. */
  7. JNIEnv* dvmCreateJNIEnv(Thread* self) {
  8. JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
  9. //if (self != NULL)
  10. //    LOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);
  11. assert(vm != NULL);
  12. JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
  13. newEnv->funcTable = &gNativeInterface;
  14. if (self != NULL) {
  15. dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
  16. assert(newEnv->envThreadId != 0);
  17. } else {
  18. /* make it obvious if we fail to initialize these later */
  19. newEnv->envThreadId = 0x77777775;
  20. newEnv->self = (Thread*) 0x77777779;
  21. }
  22. if (gDvmJni.useCheckJni) {
  23. dvmUseCheckedJniEnv(newEnv);
  24. }
  25. ScopedPthreadMutexLock lock(&vm->envListLock);
  26. /* insert at head of list */
  27. newEnv->next = vm->envList;
  28. assert(newEnv->prev == NULL);
  29. if (vm->envList == NULL) {
  30. // rare, but possible
  31. vm->envList = newEnv;
  32. } else {
  33. vm->envList->prev = newEnv;
  34. }
  35. vm->envList = newEnv;
  36. //if (self != NULL)
  37. //    LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
  38. return (JNIEnv*) newEnv;
  39. }

最重要的是gNativeInterface 我们看定义,太长了,我们找到其中的FindClass。

Cpp代码  

  1. static jclass FindClass(JNIEnv* env, const char* name) {
  2. ScopedJniThreadState ts(env);
  3. const Method* thisMethod = dvmGetCurrentJNIMethod();
  4. assert(thisMethod != NULL);
  5. Object* loader;
  6. Object* trackedLoader = NULL;
  7. if (ts.self()->classLoaderOverride != NULL) {
  8. /* hack for JNI_OnLoad */
  9. assert(strcmp(thisMethod->name, "nativeLoad") == 0);
  10. loader = ts.self()->classLoaderOverride;
  11. } else if (thisMethod == gDvm.methDalvikSystemNativeStart_main ||
  12. thisMethod == gDvm.methDalvikSystemNativeStart_run) {
  13. /* start point of invocation interface */
  14. if (!gDvm.initializing) {
  15. loader = trackedLoader = dvmGetSystemClassLoader();
  16. } else {
  17. loader = NULL;
  18. }
  19. } else {
  20. loader = thisMethod->clazz->classLoader;
  21. }
  22. char* descriptor = dvmNameToDescriptor(name);
  23. if (descriptor == NULL) {
  24. return NULL;
  25. }
  26. ClassObject* clazz = dvmFindClassNoInit(descriptor, loader);
  27. free(descriptor);
  28. jclass jclazz = (jclass) addLocalReference(ts.self(), (Object*) clazz);
  29. dvmReleaseTrackedAlloc(trackedLoader, ts.self());
  30. return jclazz;
  31. }

在dalvik/vm/Init.cpp中的方法对gVM的bootpath进行了初始化:

Java代码  

  1. static void setCommandLineDefaults()
  2. {
  3. const char* envStr = getenv("CLASSPATH");
  4. if (envStr != NULL) {
  5. gDvm.classPathStr = strdup(envStr);
  6. } else {
  7. gDvm.classPathStr = strdup(".");
  8. }
  9. envStr = getenv("BOOTCLASSPATH");
  10. if (envStr != NULL) {
  11. gDvm.bootClassPathStr = strdup(envStr);
  12. } else {
  13. gDvm.bootClassPathStr = strdup(".");
  14. }
  15. gDvm.properties = new std::vector<std::string>();
  16. /* Defaults overridden by -Xms and -Xmx.
  17. * TODO: base these on a system or application-specific default
  18. */
  19. gDvm.heapStartingSize = 2 * 1024 * 1024;  // Spec says 16MB; too big for us.
  20. gDvm.heapMaximumSize = 16 * 1024 * 1024;  // Spec says 75% physical mem
  21. gDvm.heapGrowthLimit = 0;  // 0 means no growth limit
  22. gDvm.stackSize = kDefaultStackSize;
  23. gDvm.concurrentMarkSweep = true;
  24. /* gDvm.jdwpSuspend = true; */

现在明白了,在init.rc中指定的BOOTCLASSPATH赋值给了gDvm.bootClassPathStr 。

而下面这个地方,则对FindClass进行了初始化。还是在dalvik/vm/Init.cpp中

Cpp代码  

  1. */
  2. f (!dvmFindRequiredClassesAndMembers()) {
  3. return "dvmFindRequiredClassesAndMembers failed";

initDirectMethodReferences 把方法gDvm.methDalvikSystemNativeStart_main与NativeStart进行了对应。

Java代码  

  1. { &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代码了。

时间: 2024-10-09 19:47:27

安卓高手之路之ClassLoader(二)的相关文章

安卓高手之路之 ClassLoader

我不喜欢那些泛泛而谈的去讲那些形而上学的道理,更不喜欢记那些既定的东西.靠记忆去弥补思考的人,容易陷入人云亦云的境地,最后必定被记忆所围困,而最终消亡的是创造力.希望这个高手之路系列能够记录我学习安卓的点点滴滴.从而汇成流,聚为江,成为海. 下面就结合代码分析一下ClassLoader这个东西. 安卓应用程序是一个Dalvik虚拟机,加载的是Dex格式的文件.加载Dex格式的文件从直观上理解就是ClassLoader做的事情.那么,我们就从应用程序的启动说起,因为应用程序的启动一定是与Class

安卓高手之路之ClassLoader(三)

由于看C++和C代码看得很累,很辛苦.上一章终于解脱到java代码中来了. 第一个getClassLoader发生在main的preload方法中, public static void main(String argv[]) { preload(); } Java代码   static void preload() { preloadClasses(); preloadResources(); } Java代码   private static void preloadClasses() {

安卓高手之路之 WindowManager

安卓中的画面不是纯粹由window组成.而是改成了window+view的组织模式.window是一个顶层窗口的概念.view就相当于在window内的控件.而subwindow则是依附于window的一些对话框.安卓在对window进行管理的时候,将window分为很多层,不同的层又对应于不同的window类型.下面这个图阐释了这种概念: 安卓首先将窗口按照layer分层,然后每一层又有很多window,每一个window又包含了很多的view和sublayer.这些分层的概念对于用户端是透明

Python高手之路【十二】面向对象设计模式

单例模式 单例,顾名思义单个实例. class Person: __instance = None def __init__(self): pass @staticmethod def getInstance(): if Person.__instance: return Person.__instance else: Person.__instance = Person() return Person.__instance obj1 = Person.getInstance() print(o

安卓高手之路之java层Binder

很多人一提到Binder就说代理模式,人云亦云的多,能理解精髓的少. 本篇文章就从设计角度分析一下java层BInder的设计目标,以及设计思路,设计缺陷,从而驾驭它. 对于[邦德儿]的理解, 从通信的角度来看,就是一种通信方式而已,与socket没有任何区别.客户端transact,服务端onTransact.  但是,从[邦德儿]本身来说,如果客户端和服务端在一个进程,那么再通过底层驱动去把数据转过去就显得多余了.基于这种理论,设计的时候,如果客户端和服务端在一个进程就直接函数调用,而不再通

安卓高手之路之PackageManagerservice

源码位置:frameworks/base/core/java/android/content/pm/PackageParser.java 源文件路径:android\frameworks\base\services\java\com\android\server\PackageManagerService.java 1.PackageManagerService.java 用到一个很重要的工具类,PackageParser.java. 这里面是一些 工具类 和 工具方法,辅助PackageMan

[js高手之路] html5 canvas系列教程 - arcTo(弧度与二次,三次贝塞尔曲线以及在线工具)

之前,我写了一个arc函数的用法:[js高手之路] html5 canvas系列教程 - arc绘制曲线图形(曲线,弧线,圆形). arcTo: cxt.arcTo( cx, cy, x2, y2, 半径 ) cx,cy表示控制点的坐标,x2,y2表示结束点的坐标,如果我们想画一条弧线,需要提供3个 http://pic.cnhubei.com/space.php?uid=4593&do=album&id=1092946http://pic.cnhubei.com/space.php?ui

巨人大哥谈Java工程师高手之路

巨人大哥谈Java工程师高手之路 JVM方面 JVM内存结构 堆.栈.方法区.直接内存.堆和栈区别 Java内存模型 内存可见性.重排序.顺序一致性.volatile.锁.final 垃圾回收 内存分配策略.垃圾收集器(G1).GC算法.GC参数.对象存活的判定 JVM参数及调优 Java对象模型 oop-klass.对象头 HotSpot 即时编译器.编译优化 类加载机制 classLoader.类加载过程.双亲委派(破坏双亲委派).模块化(jboss modules.osgi.jigsaw)

[js高手之路]原型对象(prototype)与原型链相关属性与方法详解

一,instanceof: instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型. 我在之前的两篇文章 [js高手之路]构造函数的基本特性与优缺点 [js高手之路]一步步图解javascript的原型(prototype)对象,原型链 已经分享过了. function CreateObj(uName) {             this.userName = uName;             this.showUserName = function