用Java这么久,你能说出Java启动过程嘛?别吹了,来看看

首先要去openJDK上下个源码。我这里下到的是openjdk-7-fcs-src-b147-27_jun_2011。

</pre>Java.exe这个程序的启动main函数在<br style="padding:0px; margin:0px; outline:none; list-style:none; border:0px none" /><strong>Java.c (hotspot\src\share\tools\launcher)</strong><br style="padding:0px; margin:0px; outline:none; list-style:none; border:0px none" />main函数的实现如下:<p></p><p style="padding-top:1em; padding-bottom:1em; margin-top:0px; margin-bottom:0px; outline:none; list-style:none; border:0px none; color:rgb(51,51,51); font-family:'Droid Sans',Arial,Verdana,sans-serif; font-size:16px; line-height:24px; orphans:2; widows:2"></p><pre name="code" class="java"> char *jarfile = 0;
    char *classname = 0;
    char *s = 0;
    char *main_class = NULL;
    int ret;
    InvocationFunctions ifn;
    jlong start, end;
    char jrepath[MAXPATHLEN], jvmpath[MAXPATHLEN];
    char ** original_argv = argv;

    if (getenv("_JAVA_LAUNCHER_DEBUG") != 0) {
        _launcher_debug = JNI_TRUE;
        printf("----_JAVA_LAUNCHER_DEBUG----\n");
    }

#ifndef GAMMA
    /*
     * 确定一下版本 是1.4 ,1.5 ,1.6
     * Make sure the specified version of the JRE is running.
     *
     * There are three things to note about the SelectVersion() routine:
     *  1) If the version running isn't correct, this routine doesn't
     *     return (either the correct version has been exec'd or an error
     *     was issued).
     *  2) Argc and Argv in this scope are *not* altered by this routine.
     *     It is the responsibility of subsequent code to ignore the
     *     arguments handled by this routine.
     *  3) As a side-effect, the variable "main_class" is guaranteed to
     *     be set (if it should ever be set).  This isn't exactly the
     *     poster child for structured programming, but it is a small
     *     price to pay for not processing a jar file operand twice.
     *     (Note: This side effect has been disabled.  See comment on
     *     bugid 5030265 below.)
     */
    SelectVersion(argc, argv, &main_class);
#endif /* ifndef GAMMA */

    /* 把参数都拿出来 */
    {
      int i;
      original_argv = (char**)JLI_MemAlloc(sizeof(char*)*(argc+1));
      for(i = 0; i < argc+1; i++)
        original_argv[i] = argv[i];
    }

   /**
    * 建立执行环境,比如看看jre 有没有安装,读一下jre的路径,jvm.cfg
    */
    CreateExecutionEnvironment(&argc, &argv,
                               jrepath, sizeof(jrepath),
                               jvmpath, sizeof(jvmpath),
                               original_argv);

    printf("Using java runtime at: %s\n", jrepath);

    ifn.CreateJavaVM = 0;
    ifn.GetDefaultJavaVMInitArgs = 0;

    //一个计时器之类的
    if (_launcher_debug)
      start = CounterGet();
    //载入JVM了 载入dll啦,这里会调用JNI_CreateJavaVM,把JVM线程跑起来
    if (!LoadJavaVM(jvmpath, &ifn)) {
      exit(6);
    }
    //计时器,开启JVM线程用了多少时间呢
    if (_launcher_debug) {
      end   = CounterGet();
      printf("%ld micro seconds to LoadJavaVM\n",
             (long)(jint)Counter2Micros(end-start));
    }

#ifdef JAVA_ARGS  /* javac, jar and friends. */
    progname = "java";
#else             /* java, oldjava, javaw and friends */
#ifdef PROGNAME
    progname = PROGNAME;
#else
    progname = *argv;
    if ((s = strrchr(progname, FILE_SEPARATOR)) != 0) {
        progname = s + 1;
    }
#endif /* PROGNAME */
#endif /* JAVA_ARGS */
    ++argv;
    --argc;

#ifdef JAVA_ARGS
    /* 处理命令行的参数了 */
    TranslateApplicationArgs(&argc, &argv);
    if (!AddApplicationOptions()) {
        exit(1);
    }
#endif

    /* 设置默认的classpath */
    if ((s = getenv("CLASSPATH")) == 0) {
        s = ".";
    }
#ifndef JAVA_ARGS
    SetClassPath(s);
#endif

    /*
     *  解析命令行参数,比如 -jar -cp -help -h -version
     *  -verbose:gc -Xdebug -Xverify -Xrunhprof 等都在这里处理的
     */
    if (!ParseArguments(&argc, &argv, &jarfile, &classname, &ret, jvmpath)) {
      exit(ret);
    }

    /* 特别处理一下 -jar的classpath */
    if (jarfile != 0) {
        SetClassPath(jarfile);
    }

    /* set the -Dsun.java.command pseudo property */
    SetJavaCommandLineProp(classname, jarfile, argc, argv);

    /* Set the -Dsun.java.launcher pseudo property */
    SetJavaLauncherProp();

    /* set the -Dsun.java.launcher.* platform properties */
    SetJavaLauncherPlatformProps();

#ifndef GAMMA
    /* Show the splash screen if needed */
    ShowSplashScreen();
#endif

    /*
     * Done with all command line processing and potential re-execs so
     * clean up the environment.
     */
    (void)UnsetEnv(ENV_ENTRY);
#ifndef GAMMA
    (void)UnsetEnv(SPLASH_FILE_ENV_ENTRY);
    (void)UnsetEnv(SPLASH_JAR_ENV_ENTRY);

    JLI_MemFree(splash_jar_entry);
    JLI_MemFree(splash_file_entry);
#endif

    /*
     * 线程的栈大小设置,没有设置就是默认的0
     */
    if (threadStackSize == 0) {
      struct JDK1_1InitArgs args1_1;
      memset((void*)&args1_1, 0, sizeof(args1_1));
      args1_1.version = JNI_VERSION_1_1;
      ifn.GetDefaultJavaVMInitArgs(&args1_1);  /* ignore return value */
      if (args1_1.javaStackSize > 0) {
         threadStackSize = args1_1.javaStackSize;
      }
    }

    { /* Java的参数 把这个参数给JavaMain方法,执行JavaMain方法 */
      struct JavaMainArgs args;

      args.argc = argc;
      args.argv = argv;
      args.jarfile = jarfile;
      args.classname = classname;
      args.ifn = ifn;
      //执行JavaMain,就是Java的主函数
      return ContinueInNewThread(JavaMain, threadStackSize, (void*)&args);
    }


ContinueInNewThread方法创建了一个线程来执行JavaMain,因此后面执行的就是JavaMain。

JavaMain是这样的:

   

   //首先是取得命令行参数 执行的classname jar之类的东西
   struct JavaMainArgs *args = (struct JavaMainArgs *)_args;
    int argc = args->argc;
    char **argv = args->argv;
    char *jarfile = args->jarfile;
    char *classname = args->classname;
    InvocationFunctions ifn = args->ifn;

    JavaVM *vm = 0;
    JNIEnv *env = 0;
    jstring mainClassName;
    jclass mainClass;
    jmethodID mainID;
    jobjectArray mainArgs;
    int ret = 0;
    jlong start, end;

    /*
     * Error message to print or display; by default the message will
     * only be displayed in a window.
     */
    char * message = "Fatal exception occurred.  Program will exit.";
    jboolean messageDest = JNI_FALSE;

    /* Initialize the virtual machine */
    //计时器又来了
    if (_launcher_debug)
        start = CounterGet();
    //初始化JVM虚拟机 这里主要去调用了 JNI_CreateJavaVM -->Threads::create_vm
    //这里做了很多事情,Threads::create_vm 以后再分析。这里只要知道把JVM起来了。
    if (!InitializeJVM(&vm, &env, &ifn)) {
        ReportErrorMessage("Could not create the Java virtual machine.",
                           JNI_TRUE);
        exit(1);
    }

    if (printVersion || showVersion) {
        PrintJavaVersion(env);
        if ((*env)->ExceptionOccurred(env)) {
            ReportExceptionDescription(env);
            goto leave;
        }
        if (printVersion) {
            ret = 0;
            message = NULL;
            goto leave;
        }
        if (showVersion) {
            fprintf(stderr, "\n");
        }
    }

    /* mainclass和jar总要指定一个吧,不然java没法跑 */
    if (jarfile == 0 && classname == 0) {
        PrintUsage();
        message = NULL;
        goto leave;
    }

#ifndef GAMMA
    FreeKnownVMs();  /* after last possible PrintUsage() */
#endif
    //由来一个计时器
    if (_launcher_debug) {
        end   = CounterGet();
        printf("%ld micro seconds to InitializeJVM\n",
               (long)(jint)Counter2Micros(end-start));
    }

    /* 拿到了应用的参数 打印一下看看 */
    if (_launcher_debug) {
        int i = 0;
        printf("Main-Class is '%s'\n", classname ? classname : "");
        printf("Apps' argc is %d\n", argc);
        for (; i < argc; i++) {
            printf("    argv[%2d] = '%s'\n", i, argv[i]);
        }
    }

    ret = 1;

    /*
     *  取得MainClass
     */
    if (jarfile != 0) {
        mainClassName = GetMainClassName(env, jarfile);
        if ((*env)->ExceptionOccurred(env)) {
            ReportExceptionDescription(env);
            goto leave;
        }
        if (mainClassName == NULL) {
          const char * format = "Failed to load Main-Class manifest "
                                "attribute from\n%s";
          message = (char*)JLI_MemAlloc((strlen(format) + strlen(jarfile)) *
                                    sizeof(char));
          sprintf(message, format, jarfile);
          messageDest = JNI_TRUE;
          goto leave;
        }
        classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
        if (classname == NULL) {
            ReportExceptionDescription(env);
            goto leave;
        }
        //载入类转为mainClass,它是jclass结构体
        mainClass = LoadClass(env, classname);
        if(mainClass == NULL) { /* exception occured */
            const char * format = "Could not find the main class: %s. Program will exit.";
            ReportExceptionDescription(env);
            message = (char *)JLI_MemAlloc((strlen(format) +
                                            strlen(classname)) * sizeof(char) );
            messageDest = JNI_TRUE;
            sprintf(message, format, classname);
            goto leave;
        }
        (*env)->ReleaseStringUTFChars(env, mainClassName, classname);
    } else {
      //前面处理jar的情况 这里处理没有指定jar的情况
      mainClassName = NewPlatformString(env, classname);
      if (mainClassName == NULL) {
        const char * format = "Failed to load Main Class: %s";
        message = (char *)JLI_MemAlloc((strlen(format) + strlen(classname)) *
                                   sizeof(char) );
        sprintf(message, format, classname);
        messageDest = JNI_TRUE;
        goto leave;
      }
      classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
      if (classname == NULL) {
        ReportExceptionDescription(env);
        goto leave;
      }
      //上面处理utf8的,我也没看过,应该不是核心 不管了,这里载入MainClass
      mainClass = LoadClass(env, classname);
      if(mainClass == NULL) { /* exception occured */
        const char * format = "Could not find the main class: %s.  Program will exit.";
        ReportExceptionDescription(env);
        message = (char *)JLI_MemAlloc((strlen(format) +
                                        strlen(classname)) * sizeof(char) );
        messageDest = JNI_TRUE;
        sprintf(message, format, classname);
        goto leave;
      }
      (*env)->ReleaseStringUTFChars(env, mainClassName, classname);
    }

    /* 找到MainClass main方法 当然这个main必须是由String[]参数的 */
    mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                       "([Ljava/lang/String;)V");
    //找不到就挂了呗
    if (mainID == NULL) {
        if ((*env)->ExceptionOccurred(env)) {
            ReportExceptionDescription(env);
        } else {
          message = "No main method found in specified class.";
          messageDest = JNI_TRUE;
        }
        goto leave;
    }

    {    /* main函数必须是public的 */
        jint mods;
        jmethodID mid;
        //这里拿出来的是 java.lang.reflect.Method
        jobject obj = (*env)->ToReflectedMethod(env, mainClass,
                                                mainID, JNI_TRUE);

        if( obj == NULL) { /* exception occurred */
            ReportExceptionDescription(env);
            goto leave;
        }
        // 一个反射 调用的 java.lang.reflect.Method.getModifiers
        // 找到修饰符 看看是不是public
        mid =
          (*env)->GetMethodID(env,
                              (*env)->GetObjectClass(env, obj),
                              "getModifiers", "()I");
        if ((*env)->ExceptionOccurred(env)) {
            ReportExceptionDescription(env);
            goto leave;
        }
        // public就是1,这个看java的代码里就能找到所有的定义
        mods = (*env)->CallIntMethod(env, obj, mid);
        if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */
            message = "Main method not public.";
            messageDest = JNI_TRUE;
            goto leave;
        }
    }

    /* 把Java程序的参数都放好 */
    mainArgs = NewPlatformStringArray(env, argv, argc);
    if (mainArgs == NULL) {
        ReportExceptionDescription(env);
        goto leave;
    }

    /* 调用Java的Main方法 */
    (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);

    /*
     * 
     * 遇到异常返回值就是非0
     */
    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;

    /*
     * Detach the main thread so that it appears to have ended when
     * the application's main method exits.  This will invoke the
     * uncaught exception handler machinery if main threw an
     * exception.  An uncaught exception handler cannot change the
     * launcher's return code except by calling System.exit.
     */
    if ((*vm)->DetachCurrentThread(vm) != 0) {
        message = "Could not detach main thread.";
        messageDest = JNI_TRUE;
        ret = 1;
        goto leave;
    }

    message = NULL;

 leave:
    /*
     * 这里就是退出了
     * Wait for all non-daemon threads to end, then destroy the VM.
     * This will actually create a trivial new Java waiter thread
     * named "DestroyJavaVM", but this will be seen as a different
     * thread from the one that executed main, even though they are
     * the same C thread.  This allows mainThread.join() and
     * mainThread.isAlive() to work as expected.
     */
    (*vm)->DestroyJavaVM(vm);

    if(message != NULL && !noExitErrorMessage)
      ReportErrorMessage(message, messageDest);
    return ret;


如果您会Java,但是还不会JVM,还看不懂JVM,那么来学习下葛老师开设的深入浅出JVM课程吧。http://www.dataguru.cn/myclassnew.php?mod=new_basicforlesson&op=basic&lessonid=208

或者加入QQ 群397196583,一起来讨论JVM看不通看不懂的地方。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值