zoukankan      html  css  js  c++  java
  • 跑Java -jar somefile.jar时会发生什么(一个)


    最近阅读JVM源代码。一些想法写Blog分享。于是,他开了这么一个新课题。


    第一篇文章取名字的时候让我很困惑,我代码的阅读是从Launcher開始入手的,也就是Java.exe(假设是windows平台的话)相应的相关代码,但我又不能取“JVM启动过程分析”之类的名字,由于从分析主流程的角度来讲还深不到这个层次。所以就暂且起了这么一个奇怪的名字。


    这个系列假设能继续下去的话,不加特殊说明,使用的JDK和JVM版本号均为8u20,下载地址来自OpenJDK:http://hg.openjdk.java.net/jdk8u


    本人是一个JAVA程序猿,在分析JVM大量C/C++时难免会有不妥当的地方,还希望各位读者指正。

    介于时间有涯而代码“无涯”。详细的实现细节不可能面面俱到。仅仅着重分析和看懂大致流程和机制,假设有比較重要的细节遗漏之处。欢迎留言讨论。


    在这个系列中,不论什么对源文件位置的描写叙述都使用相对路径。当中jdk/代表放置Jdk源代码的根文件夹,hotspot/代表放置jvm源代码的根文件夹。


    一、Launcher代码分析

    (1)Main.c中的main

    位置:jdk/src/bin/main.c 

    当我们调用java命令时。首先肯定进入的是C/C++的main函数,就像若干年前我写的那个helloworld一样。

    这个main函数位于jdk/src/bin/main.c。在JDK8中是放置在这个位置。在曾经较老版本号是放在JVM相关代码中的。

    main.c中差点儿没有实质的逻辑,主要是复制一些參数,处理在windows平台中的一些调用,之后就把各參数传递给JLI_launch进行运行。相关代码在第125行。

    return JLI_Launch(margc, margv,
                       sizeof(const_jargs) / sizeof(char *), const_jargs,
                       sizeof(const_appclasspath) / sizeof(char *), const_appclasspath,
                       FULL_VERSION,
                       DOT_VERSION,
                       (const_progname != NULL) ? const_progname : *margv,
                       (const_launcher != NULL) ? const_launcher : *margv,
                       (const_jargs != NULL) ? JNI_TRUE : JNI_FALSE,
                       const_cpwildcard, const_javaw, const_ergo_class);



    (2)java.c中的JLI_Launch

    位置:jdk/src/bin/java.c

    java.c中依据凝视能够大概看出这个函数的各參数含义:

    JLI_Launch(int argc, char ** argv,              /* main argc, argc */
            int jargc, const char** jargv,          /* java args */
            int appclassc, const char** appclassv,  /* app classpath */
            const char* fullversion,                /* full version defined */
            const char* dotversion,                 /* dot version defined */
            const char* pname,                      /* program name */
            const char* lname,                      /* launcher name */
            jboolean javaargs,                      /* JAVA_ARGS */
            jboolean cpwildcard,                    /* classpath wildcard*/
            jboolean javaw,                         /* windows-only javaw */
            jint ergo                               /* ergonomics class policy */

    在第236行调用系统函数获取环境变量,给jrepath,jvmpath和jvmcfg赋值。(每一个Java基础教程里设置的环境变量在这里起作用)

        CreateExecutionEnvironment(&argc, &argv,
                                   jrepath, sizeof(jrepath),
                                   jvmpath, sizeof(jvmpath),
                                   jvmcfg,  sizeof(jvmcfg));

    第248行载入jvm.dll这个文件。仅仅是载入文件到内存,并没有运行不论什么操作,同一时候给这个ifn结构体赋值(依据dll抽取函数调用地址赋值),ifn结构体包括三个关键的函数指针。

    if (!LoadJavaVM(jvmpath, &ifn)) {
            return(6);
        }
    Ifn结构例如以下:

    typedef struct {
        CreateJavaVM_t CreateJavaVM;
        GetDefaultJavaVMInitArgs_t GetDefaultJavaVMInitArgs;
        GetCreatedJavaVMs_t GetCreatedJavaVMs;
    } InvocationFunctions;
    依据函数名能够大概猜到,第一个是创建虚拟机。第二个是获取初始參数,第三个是创建非常多虚拟机?(vms代表虚拟机的复数形式?我猜的。。)。

    之后又对參数进行了一些额外的处理(包含获取classpath、打印调试信息、加入额外的參数等等)。在299行调用JVMInit初始化虚拟机

        return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);


    (3)Java.c中的JVMInit

    位置:jdk/src/bin/java.c

    代码非常少,首先打印一下信息,然后调用ConitueInNewThread函数,从函数名也能够看出,会启用一个新线程建立JVM。

    int
    JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
            int argc, char **argv,
            int mode, char *what, int ret)
    {
        ShowSplashScreen();
        return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
    }


    (4)Java.c中的ContinueInNewThread

    位置:jdk/src/bin/java.c

    这个函数依旧没做啥东西,就是封装了一下參数,然后委派给ContinueInNewThread0。

    而ContinueInNewThread0开启一个新的线程,运行JavaMain函数。


    (5)Java.c中的JavaMain

    位置:jdk/src/bin/java.c

    首先在第371行初始化JVM。而InitializeJVM函数做的工作就是调用ifn->createJavaVM,详细的JVM启动过程水太深因此不在这里进行分析。假设初始化成功,则会给vm对象和env对象赋值,当中env是一个很重要的对象,进行JNI调用的时候会频繁用到。

    if (!InitializeJVM(&vm, &env, &ifn)) {
            JLI_ReportErrorMessage(JVM_ERROR1);
            exit(1);
        }
    之后检查一下是否传入了Jar文件或者一个类名,假设没有的话就打印一下Usage

    之后第439行获取主类,获取主类的方式挺有意思的。在后面会进行分析。

    接着看看是否抛异常,假设抛异常就直接退出了。

        mainClass = LoadMainClass(env, mode, what);
        CHECK_EXCEPTION_NULL_LEAVE(mainClass);


    随后针对JavaFX载入一下东西(假设须要的话),然后获取main这种方法的ID,组合參数,并Invoke。水到渠成。

    当中每一步都检查是否有异常抛出。

     mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                           "([Ljava/lang/String;)V");
        CHECK_EXCEPTION_NULL_LEAVE(mainID);
    
        /* Build platform specific argument array */
        mainArgs = CreateApplicationArgs(env, argv, argc);
        CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
    
        /* Invoke main method. */
        (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
    
        /*
         * The launcher's exit code (in the absence of calls to
         * System.exit) will be non-zero if main threw an exception.
         */
        ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;



    (未完待续)






    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    delphi7在windows server 2003企业版上不能打开项目的选项(Options)窗口的解决方法
    简单的两个字“谢谢”,会让我坚持我的写作,我也要谢谢你们
    F41GUT 安装Windows server 2003系统后无法安装显卡驱动的解决办法
    远程桌面无法登录windows server 2003服务器
    F41GUT 安装Windows server 2003系统后无法安装显卡驱动的解决办法
    MS SQL Server 2000版在windows server 2003企业版系统上运行时造成数据库suspect的解决方法
    delphi7在windows server 2003企业版上不能打开项目的选项(Options)窗口的解决方法
    远程桌面无法登录windows server 2003服务器
    MS SQL Server 2000版在windows server 2003企业版系统上运行时造成数据库suspect的解决方法
    关于ajax 和josn
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4710717.html
Copyright © 2011-2022 走看看