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;



    (未完待续)






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

  • 相关阅读:
    SOJ4478 Easy Problem II(模拟、栈)
    SOJ4480 Easy Problem IV (并查集)
    暴力枚举法总结
    区间DP学习总结
    51nod 1019 逆序数(逆序数+离散化)
    win7系统查看硬盘序列号步骤
    雷达图制作方法
    matlab更改打开时候默认路径
    excel多组数据散点图生成
    EndNote(三)之中文引文导入方式
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4710717.html
Copyright © 2011-2022 走看看