3. 初窥全貌 - main方法执行全流程

0. 前言

一个类被编译为class file之后,使用java命令去执行,暂时抛开OS层面的syscall 及 glibc的入口函数, java中的main方法执行,经历了什么样的过程?要执行main方法,必须要有vm支持,那vm又是如何去构建的?

本章我们把握整体流程,建立一个初步认识。


1. 整体流程

1.main()    main.c
        1.JLI_Launch()    java.c
                1.1 CreateExecutionEnvironment()      java_md_solinux.c
                        SetExecname()                              java_md_solinux.c              
                1.2 SetJvmEnvironment()                     java.c
                1.3 LoadJavaVM()                                java_md_solinux.c 通过syscall加载libjvm.so jvm.dll
                1.4 SetClassPath(..)                             设置目标为class文件或jar包路径
                1.5 JvmInit()                                          java_md_solinux.c
                        1.5.1 ContinueInNewThread()      java.c
                        1.5.2 ContinueInNewThread0()    java_md_solinux.c, 创建并等待“main线程"运行结束。java栈的大小在linux64位下默认1024k,32位默认320k.
    

2.JavaMain()                                 java.c  main线程的入口
        2.1 InitializeJVM()                 java.c  初始化vm        
                JNI_CreateJavaVM()    jni.cpp
                        create_vm()           thread.cpp  创建vm的主要工作都在这个函数里完成      
        2.2 LoadMainClass()             加载main class
        2.3 CallStaticVoidMethod()   真正去执行java类中的main函数

2. main() main.c - 一切从这里开始

启动Java程序,首先需执行class文件。

使用如下命令即可在Linux系统的shell环境下启动Java程序

java [options] xxx.class param1 param2 ... paramn

当执行命令时,shell会创建新进程来执行该命令,由于JVM是用C/C++实现的,我们只需定位到main函数即可。至于glibc的入口函数,此处不再赘述。

main.c - main(..) / WinMain(..)

openjdk-jdk8u/jdk/src/share/bin/main.c

openjdk-jdk8u/jdk/src/share/bin/main.c

// 这里我们忽略掉源代码中关于windows部分的编译选项

int main(int argc, char **argv) 
{      
    int margc;     // 命令参数个数,从java开始数,空格隔开,假设命令是:java xxx.class 1 2 3,那么此时margc = 5    
    char** margv;  // 参数指针
    const jboolean const_javaw = JNI_FALSE;
    
    margc = argc;
    margv = argv;
    
    // 调用java.c的JLI_Launch函数
    return JLI_Launch(margc, margv,  
        sizeof(const_jargs) / sizeof(char *),         // const_jargs字符串个数 
        const_jargs,                                  // 编译选项[JAVA_ARGS],默认为空,一般只有java tools编译时使用JAVA_ARGS
        sizeof(const_appclasspath) / sizeof(char *),  // const_appclasspath的个数
        const_appclasspath,                           // 1. 编译选项[JAVA_ARGS][APP_CLASSPATH],为APP_CLASSPATH选项的值
                                                      // 2. 只有编译选项[JAVA_ARGS], 默认为{ "/lib/tools.jar", "/classes" }  
                                                      // 3. 为空    
        FULL_VERSION,                                 // 编译选项[DFULL_VERSION,编译时必须填,否则编译不过
        DOT_VERSION,                                  // 编译选项 [JDK_MAJOR_VERSION] "." [JDK_MINOR_VERSION] 组成,编译时必须填,否则不通过
        (const_progname != NULL) ? const_progname : *margv, // 编译选项[JAVA_ARGS] 或 [PROGNAME]确定,这里是java
        (const_launcher != NULL) ? const_launcher : *margv, // 编译选项[LAUNCHER_NAME],这里是java          
        (const_jargs != NULL) ? JNI_TRUE : JNI_FALSE,       // 在java 这里是false 
        const_cpwildcard, // 编译选项[EXPAND_CLASSPATH_WILDCARDS]决定,定义EXPAND_CLASSPATH_WILDCARDS 时为true,否则为false。 这里是true
        const_javaw,      // 编译选项[JAVAW决定,windows]下为true,linux下为false。
        const_ergo_class);// 编译选项[NEVER_ACT_AS_SERVER_CLASS_MACHINE] / [ALWAYS_ACT_AS_SERVER_CLASS_MACHINE],决定ergo_policy,可以决定jvm运行哪种模式下  
}

TIP:JDK编译选项

编译选项

在JDK的bin目录下,几乎所有的命令程序 都是通过同一个main函数编译生成的。

这些命令包括java、javac、jconsole、jps、jmap和jhat等。

根据编译时选项的不同,这些命令在执行时会进行不同的处理。具体的编译配置信息可参见附录章节。

$(eval $(call SetupLauncher,
        java, \                                      #1  launcher 名称
        -DEXPAND_CLASSPATH_WILDCARDS,\               #2  CFLAGS
        , \                                          #3  LDFLAGS
        , \                                          #4  LDFLAGS_SUFFIX_posix
        user32.lib comctl32.lib, \                   #5  LDFLAGS_SUFFIX_windows 
        $(JDK_OUTPUTDIR)/objs/jli_static.lib,\       #6  optional Windows JLI library (full path) : windows jli库的全路径  
        $(JAVA_RC_FLAGS), \                          #7  optional Windows resource (RC) flags     : windows 资源文件标识
        $(JDK_TOPDIR)/src/windows/resource/java.rc,\ #8  optional Windows version resource file (.rc) : windows 版本资源文件(.rc)
        $(JDK_OUTPUTDIR)/objs/java_objs,\            #9  different output dir : 不同的输出目录
        true                                         #10 if set, link statically with c runtime on windows. : 如果设置参数10 , 静态链接c运行时,在windows上.
    )
)
# jmap
$(eval $(call SetupLauncher,
        jmap, 
        -DJAVA_ARGS='{ "-J-ms8m"$(COMMA) 
        "-J-Dsun.jvm.hotspot.debugger.useProcDebugger"$(COMMA) 
        "-J-Dsun.jvm.hotspot.debugger.useWindbgDebugger"$(COMMA) 
        "sun.tools.jmap.JMap"$(COMMA) }' 
        -DAPP_CLASSPATH='{ "/lib/tools.jar"$(COMMA) "/lib/sa-jdi.jar"$(COMMA) "/classes" }' ,
        ,,,,,,,,
        Info-privileged.plist))     # Parameter 11 if set, override plist file on macosx. : 如果设置,覆盖 plist文件在 macosx上

// windows 上的java
ifeq ($(OPENJDK_TARGET_OS), windows)
    $(eval $(call SetupLauncher,javaw, 
    -DJAVAW -DEXPAND_CLASSPATH_WILDCARDS,,,user32.lib comctl32.lib, 
    $(JDK_OUTPUTDIR)/objs/jli_static.lib, $(JAVA_RC_FLAGS), 
    $(JDK_TOPDIR)/src/windows/resource/java.rc,,true))
endif

FULL_VERSION 和 DOT_VERSION

分别表示jdk的版本的2种表现形式,看图示

JLI_Launch() java.c

openjdk-jdk8u/jdk/src/share/bin/java.c

jdk/src/share/bin/java.c
int
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* p
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TCF000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值