java文件的运行

运行java文件,首先要有程序入口,即:public static void main(String[] args){}方法,当有类有这个方法后,可以用javac命令编译并用java命令(已配置好java环境变量)来执行该文件。

 

环境变量作用:当配置java环境变量后,输入java命令则会使windows直接在变量里配置的目录中找到java.exe运行。

 

本文分析的源码为jdk8源码

运行java命令后,jdk8启动入口:main.c就会执行

java文件的运行_第1张图片

可以看到这个文件在非win32位下几乎就是直接调用JLI_Launch方法!进入同目录下的java.c可以看到JLI_Launch方法的实现,具体代码如下:

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 */

)

{

int mode = LM_UNKNOWN;

char *what = NULL;

char *cpath = 0;

char *main_class = NULL;

int ret;

InvocationFunctions ifn;

jlong start, end;

char jvmpath[MAXPATHLEN];

char jrepath[MAXPATHLEN];

char jvmcfg[MAXPATHLEN];

 

_fVersion = fullversion;

_dVersion = dotversion;

_launcher_name = lname;

_program_name = pname;

_is_java_args = javaargs;

_wc_enabled = cpwildcard;

_ergo_policy = ergo;

 

InitLauncher(javaw);

DumpState();

if (JLI_IsTraceLauncher()) {

int i;

printf("Command line args:\n");

for (i = 0; i < argc ; i++) {

printf("argv[%d] = %s\n", i, argv[i]);

}

AddOption("-Dsun.java.launcher.diag=true", NULL);

}

 

/*

* 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);

 

CreateExecutionEnvironment(&argc, &argv,

jrepath, sizeof(jrepath),

jvmpath, sizeof(jvmpath),

jvmcfg, sizeof(jvmcfg));

 

if (!IsJavaArgs()) {

SetJvmEnvironment(argc,argv);

}

 

ifn.CreateJavaVM = 0;

ifn.GetDefaultJavaVMInitArgs = 0;

 

if (JLI_IsTraceLauncher()) {

start = CounterGet();

}

 

if (!LoadJavaVM(jvmpath, &ifn)) {

return(6);

}

 

if (JLI_IsTraceLauncher()) {

end = CounterGet();

}

 

JLI_TraceLauncher("%ld micro seconds to LoadJavaVM\n",

(long)(jint)Counter2Micros(end-start));

 

++argv;

--argc;

 

if (IsJavaArgs()) {

/* Preprocess wrapper arguments */

TranslateApplicationArgs(jargc, jargv, &argc, &argv);

if (!AddApplicationOptions(appclassc, appclassv)) {

return(1);

}

} else {

/* Set default CLASSPATH */

cpath = getenv("CLASSPATH");

if (cpath == NULL) {

cpath = ".";

}

SetClassPath(cpath);

}

 

/* Parse command line options; if the return value of

* ParseArguments is false, the program should exit.

*/

if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath))

{

return(ret);

}

 

/* Override class path if -jar flag was specified */

if (mode == LM_JAR) {

SetClassPath(what); /* Override class path */

}

 

/* set the -Dsun.java.command pseudo property */

SetJavaCommandLineProp(what, argc, argv);

 

/* Set the -Dsun.java.launcher pseudo property */

SetJavaLauncherProp();

 

/* set the -Dsun.java.launcher.* platform properties */

SetJavaLauncherPlatformProps();

 

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

}

 

可以看到JLI_Launch方法中声明了变量后,运行如下一段代码:

java文件的运行_第2张图片

1.上面这段代码主要就是来设置是否打印debug信息。

 

java文件的运行_第3张图片

java文件的运行_第4张图片

 

2.接着就是运行上面这段的SelectVersion方法,它就是选择JRE版本,规则是:若环境变量指定了JRE版本,则用这个指定的版本,若没有指定,则根据运行时的参数来搜索目录或jar文件解析来选择版本。

 

java文件的运行_第5张图片

java文件的运行_第6张图片

 

3.CreateExecutionEnvironment就是创建执行的环境,代码如上所示,这个方法会确定需要的物理模型,比如64位还是32位、JRE路径、JVM类型和路径等。其中,JVM类型是ReadKnownVMs方法读取jvm.cfg文件获知的,若java命令中指定了jvm类型则以指定的参数(如:-XXaltjvm)为准!JVM路径是根据JVM.dll文件获知的

 

java文件的运行_第7张图片

java文件的运行_第8张图片

4.当指定非java参数如-XX:NativeMemoryTracking=value(jvm参数)这个参数后,则就会启用跟踪java的内存使用的配置,默认value为off,不启用

 

java文件的运行_第9张图片

java文件的运行_第10张图片

5.LoadJavaVM主要加载jvm运行所需要的类库,将JVM.dll中定义的JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs函数绑定到ifn变量的CreateJavaVM与GetDefaultJavaVMInitArgs指针上

 

java文件的运行_第11张图片

java文件的运行_第12张图片

6.解析参数。若有java参数,就用TranslateApplicationArgs解析 -cp 参数,AddApplicationOptions解析-Denv.class.path等这样的参数,否则设置默认classpath。然后ParseArguments几乎将所有命令中的参数解析

 

java文件的运行_第13张图片

java文件的运行_第14张图片

java文件的运行_第15张图片

 

7.可以看出最后JVMInit方法就是调用ContinueInNewThread来设置线程栈大小以及创建新线程来创建jvm以及调用main方法。设置线程栈:若启动参数未设置-Xss的值,threadStackSize会一直为0,直到上面这段代码,当threadStackSize为0,则会通过GetDefaultJavaVMInitArgs方法即调用JVM.dll的JNI_GetDefaultJavaVMInitArgs方法来确定线程栈大小

 

java文件的运行_第16张图片

java文件的运行_第17张图片

 

ContinueInNewThread调用了JavaMain和ContinueInNewThread0方法。

JavaMain方法主要步骤:初始化jvm(InitializeJVM)、验证加载主类(LoadMainClass)、获取main方法ID(GetStaticMethodID)、加载main方法(CallStaticVoidMethod)

java文件的运行_第18张图片

java文件的运行_第19张图片

 

InitializeJVM方法主要调用CreateJavaVM方法,CreateJavaVM又是调用JNI_CreateJavaVM方法,在JNI_CreateJavaVM主要步骤如下:

1.使用Atomic::xchg来同步产生唯一一个jvm

2.主要调用create_vm来初始化jvm,比如初始化 版本号、流模块、jvm启动器参数等

3.给jvm的线程初始化jni环境(jni主要用于不同语言间的函数调用)

4.启动跟踪时间进程

5.初始化jvmti环境(jvmti主要用于提供性能分析,debug、内存管理等工具接口)

6.加载java.lang.String等系统类

7.设置jvm线程等状态

 

java文件的运行_第20张图片

LoadMainClass是验证并加载主类

 

java文件的运行_第21张图片

验证并加载main的class后就是加载main方法了,具体如上所示。

 

你可能感兴趣的:(jvm机制原理)