深入理解Java之jvm启动流程

网友投稿 326 2022-11-03


深入理解Java之jvm启动流程

jvm是java的核心运行平台,自然是个非常复杂的系统。当然了,说jvm是个平台,实际上也是个泛称。准确的说,它是一个java虚拟机的统称,它并不指具体的某个虚拟机。所以,谈到java虚拟机时,往往我们通常说的都是一些规范性质的东西。

那么,如果想要研究jvm是如何工作的,就不能是泛泛而谈了。我们必须要具体到某个指定的虚拟机实现,以便说清其过程。

1. 说说openjdk

因为java实际上已经被oracle控制,而oracle本身是个商业公司,所以从某种程度上说,这里的java并不是完全开源的。我们称官方的jdk为oraclejdk. 或者叫 hotspot vm

与此同时,社区维护了一个完全开源的版本,openjdk。这两个jdk实际上,大部分是相同的,只是维护的进度不太一样,以及版权归属不一样。

所以,如果想研究jvm的实现,那么基于openjdk来做,是比较明智的选择。

如果想了解openjdk是如何设计的,以及它有什么高级特性,以及各种最佳实践,那么买一本书是最佳选择。

如果业有余力,想去了解了解源码的,那么可以到官网查看源码。openjdk8的源码地址为: http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/ 因为是国外网站的原因,速度不会很快。所以只是在网站上查看源码,还是有点累的。另外,没有ide的帮助,估计很少有人能够坚持下去。另外的下载地址,大家可以网上搜索下,资源总是有的,国人链接速度快。多花点心思找找。

当然要说明的一点是:一个没有设计背景,没有框架概念的源码阅读,都是而流氓。那样的工作,就像是空中楼阁,并不让人踏实。

2. 谈谈C语言

C语言,一般作为我们的大学入门语言,或多或少都接触过。但要说精通,可能就是很少一部分人了。但我要说的是,只要学过C语言,对于大部分的程序阅读,基本上就不是问题了。

openjdk的实现中,其核心的一部分就是使用C语言写的,当然其他很多语言也是一样的。所以,C语言相当重要,在底层的世界里。这里只是说它重要,但并不代表它就一定最厉害,即不是写C语言的GG就比写JAVA的JJ厉害了。因为,工作不分高低,语言同样。只是各有所长罢了。重点不是在这里,在于思想。

C语言的编程几大流程:写代码(最核心)、编译、链接(最麻烦)、运行。

当然,最核心的自然是写代码。不对,最核心的是:做设计。

C语言中,以一个main()函数为入口,编写各种逻辑后,通过调用和控制main()方法,实现各种复杂逻辑。

所以,要研究一个项目,首先就是要找到其入口。然后根据目的,再进行各功能实现的通路学习。

C语言有极其灵活的语法,超级复杂的指针设计,以及各类似面向对象思想的结构体,以及随时可能操作系统获取信息的能力(各种链接)。所以,导致C语言有时确实比较难以读懂。这也是没办法的事,会很容易,精却很难。这是亘古不变的道理。是一个选择题,也是一道应用题。

一句话,会一点,就够吃瓜群众使用了。

3. openjdk的入口

上面说到,要研究一个C项目,首要就是找到其入口。那么,openjdk的入口在哪呢?

是在 share/bin/main.c 中,main()方法就是其入口。这个文件命名,够清晰了吧,明眼人一看就知道了。哈哈,不过一般地,我们还是需要通过查资料才知晓。

main.c是jvm的唯一main方法入口,其中,jdk被编译出来之后,会有许多的工作箱,如jmap,jps,jstack.... 这些工具箱的入口,实际也是这个main, 只是它们包含了不同的子模块,从而达到不同工具的目的。

main.c的内容也不多,主要它也只是一个框架,为屏蔽各系统的差异。它的存在,主要是为引入 JLI_LAUNCH() 方法,相当于定义自己的main()方法。

/*

* This file contains the main entry point into the launcher code

* this is the only file which will be repeatedly compiled by other

* tools. The rest of the files will be linked in.

*/

#include "defines.h"

#ifdef _MSC_VER

#if _MSC_VER > 1400 && _MSC_VER < 1600

/*

* When building for Microsoft Windows, main has a dependency on msvcr??.dll.

*

* When using Visual Studio 2005 or 2008, that must be recorded in

* the [java,javaw].exe.manifest file.

*

* As of VS2010 (ver=1600), the runtimes again no longer need manifests.

*

* Reference:

* C:/Program Files/Microsoft SDKs/Windows/v6.1/include/crtdefs.h

*/

#include

#ifdef _M_IX86

#pragma comment(linker,"/manifestdependency:\"type='win32' " \

"name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".CRT' " \

"version='" _CRT_ASSEMBLY_VERSION "' " \

"processorArchitecture='x86' " \

"publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "'\"")

#endif /* _M_IX86 */

//This may not be necessary yet for the Windows 64-bit build, but it

//will be when that build environment is updated. Need to test to see

//if it is harmless:

#ifdef _M_AMD64

#pragma comment(linker,"/manifestdependency:\"type='win32' " \

"name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".CRT' " \

"version='" _CRT_ASSEMBLY_VERSION "' " \

"processorArchitecture='amd64' " \

"publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "'\"")

#endif /* _M_AMD64 */

#endif /* _MSC_VER > 1400 && _MSC_VER < 1600 */

#endif /* _MSC_VER */

/*

* Entry point.

*/

// 定义入口函数,JAVAW模式下使用 WinMain(), 否则使用 main()

#ifdef JAVAW

char **__initenv;

int WINAPI

WinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow)

{

int margc;

char** margv;

const jboolean const_javaw = JNI_TRUE;

__initenv = _environ;

#else /* JAVAW */

int

main(int argc, char **argv)

{

int margc;

char** margv;

const jboolean const_javaw = JNI_FALSE;

#endif /* JAVAW */

#ifdef _WIN32

// windows下的参数获取

{

int i = 0;

if (getenv(JLDEBUG_ENV_ENTRY) != NULL) {

printf("Windows original main args:\n");

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

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

}

}

}

JLI_CmdToArgs(GetCommandLine());

margc = JLI_GetStdArgc();

// add one more to mark the end

margv = (char **)JLI_MemAlloc((margc + 1) * (sizeof(char *)));

{

int i = 0;

StdArg *stdargs = JLI_GetStdArgs();

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

margv[i] = stdargs[i].arg;

}

margv[i] = NULL;

}

#else /* *NIXES */

// 各种linux平台上的参数,直接取自main入参

margc = argc;

margv = argv;

#endif /* WIN32 */

// 核心: 重新定义入口方法为: JLI_Launch()

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

}

因为java语言被设计成跨平台的语言,那么如何跨平台呢?因为平台差异总是存在的,如果语言本身不关注平台,那么自然是有人在背后关注了平台,从而屏蔽掉了差异。是了,这就是虚拟机存在的意义。因此,在入口方法,我们就可以看到,它一上来就关注平台差异性。这是必须的。

4. openjdk的启动流程

有了上面的入口知识,好像是明白了一些道理。但是好像还是没有达到要理解启动过程的目的。不急,且听我慢慢道来。

我们启动一个虚拟机时,一般是使用 java -classpath:xxx xx.xx , 或者是 java -jar xx.jar 。 具体怎么用无所谓,重点是我们都是 java这个应用程序启动的虚拟机。因此,我们便知道 java 程序,是我们启动jvm的核心开关。

4.0. jvm启动流程框架

废话不多说,java.c, 是我们要研究的重要文件。它将是一个控制启动流程的实现超人。而它的入口,就是在main()中的定义 JLI_Launch(...) , 所以让我们一睹真容。

// share/bin/java.c

/*

* Entry point.

*/

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

*/

// 解析命令行参数,选择一jre版本

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(); // 记录启动时间

}

// 加载VM, 重中之重

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

// 初始化jvm,即加载java程序开始,应用表演时间到

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

}

以上就是整个jvm虚拟机的启动过程框架了,基本上跑不掉几个点,就是解析命令行参数,设置参数到某范围内或者环境变量中。加载必要模块,传递变量存储。初始化系统。解析用户系统实现。当然一般地,就是会实现系统主循环,这个动作是由使用系统完成的,jvm只负责执行即可。

因为我们只是想了解大概,所以不以为然,只是其中任何一个点都足够研究很久很久了。抛开那些不说,捡个芝麻先。需要明白:懂得许多的道理却依然过不好这一生。只能安心做个吃瓜群众。

下面,就一些细节点,我们可以视兴趣,稍微深入了解下!

4.1. jre版本选择过程

以上框架中,几个重要的节点,我们可以再细化下实现。细节就不说,太复杂。首先,就是如何确定当前系统使用的jre版本,这很重要,它决定了应用系统是否可以运行的问题。因为有时候,系统的使用者并非开发者,一定存在正确的jre版本。没有jre的环境,所有java执行就会是一句空谈。

// java.c

/*

* The SelectVersion() routine ensures that an appropriate version of

* the JRE is running. The specificatiopcFxYSkn for the appropriate version

* is obtained from either the manifest of a jar file (preferred) or

* from command line options.

* The routine also parses splash screen command line options and

* passes on their values in private environment variables.

*/

static void

SelectVersion(int argc, char **argv, char **main_class)

{

char *arg;

char **new_argv;

char **new_argp;

char *operand;

char *version = NULL;

char *jre = NULL;

int jarflag = 0;

int headlessflag = 0;

int restrict_search = -1; /* -1 implies not known */

manifest_info info;

char env_entry[MAXNAMELEN + 24] = ENV_ENTRY "=";

char *splash_file_name = NULL;

char *splash_jar_name = NULL;

char *env_in;

int res;

/*

* If the version has already been selected, set *main_class

* with the value passed through the environment (if any) and

* simply return.

*/

// _JAVA_VERSION_SET=

if ((env_in = getenv(ENV_ENTRY)) != NULL) {

if (*env_in != '\0')

*main_class = JLI_StringDup(env_in);

return;

}

/*

* Scan through the arguments for options relevant to multiple JRE

* support. For reference, the command line syntax is defined as:

*

* SYNOPSIS

* java [options] class [argument...]

*

* java [options] -jar file.jar [argument...]

*

* As the scan is performed, make a copy of the argument list with

* the version specification options (new to 1.5) removed, so that

* a version less than 1.5 can be exec'd.

*

* Note that due to the syntax of the native Windows interface

* CreateProcess(), processing similar to the following exists in

* the Windows platform specific routine ExecJRE (in java_md.c).

* Changes here should be reproduced there.

*/

new_argv = JLI_MemAlloc((argc + 1) * sizeof(char*));

new_argv[0] = argv[0];

new_argp = &new_argv[1];

argc--;

argv++;

while ((arg = *argv) != 0 && *arg == '-') {

if (JLI_StrCCmp(arg, "-version:") == 0) {

version = arg + 9;

} else if (JLI_StrCmp(arg, "-jre-restrict-search") == 0) {

restrict_search = 1;

} else if (JLI_StrCmp(arg, "-no-jre-restrict-search") == 0) {

restrict_search = 0;

} else {

if (JLI_StrCmp(arg, "-jar") == 0)

jarflag = 1;

/* deal with "unfortunate" classpath syntax */

if ((JLI_StrCmp(arg, "-classpath") == 0 || JLI_StrCmp(arg, "-cp") == 0) &&

(argc >= 2)) {

*new_argp++ = arg;

argc--;

argv++;

arg = *argv;

}

/*

* Checking for headless toolkit option in the some way as AWT does:

* "true" means true and any other value means false

*/

if (JLI_StrCmp(arg, "-Djava.awt.headless=true") == 0) {

headlessflag = 1;

} else if (JLI_StrCCmp(arg, "-Djava.awt.headless=") == 0) {

headlessflag = 0;

} else if (JLI_StrCCmp(arg, "-splash:") == 0) {

splash_file_name = arg+8;

}

*new_argp++ = arg;

}

argc--;

argv++;

}

if (argc <= 0) { /* No operand? Possibly legit with -[full]version */

operand = NULL;

} else {

argc--;

*new_argp++ = operand = *argv++;

}

while (argc-- > 0) /* Copy over [argument...] */

*new_argp++ = *argv++;

*new_argp = NULL;

/*

* If there is a jar file, read the manifest. If the jarfile can't be

* read, the manifest can't be read from the jar file, or the manifest

* is corrupt, issue the appropriate error messages and exit.

*

* Even if there isn't a jar file, construct a manifest_info structure

* containing the command line information. It's a convenient way to carry

* this data around.

*/

if (jarflag && operand) {

if ((res = JLI_ParseManifest(operand, &info)) != 0) {

if (res == -1)

JLI_ReportErrorMessage(JAR_ERROR2, operand);

else

JLI_ReportErrorMessage(JAR_ERROR3, operand);

exit(1);

}

/*

* Command line splash screen option should have precedence

* over the manifest, so the manifest data is used only if

* splash_file_name has not been initialized above during command

* line parsing

*/

if (!headlessflag && !splash_file_name && info.splashscreen_image_file_name) {

splash_file_name = info.splashscreen_image_file_name;

splash_jar_name = operand;

}

} else {

info.manifest_version = NULL;

info.main_class = NULL;

info.jre_version = NULL;

info.jre_restrict_search = 0;

}

/*

* Passing on splash screen info in environment variables

*/

if (splash_file_name && !headlessflag) {

char* splash_file_entry = JLI_MemAlloc(JLI_StrLen(SPLASH_FILE_ENV_ENTRY "=")+JLI_StrLen(splash_file_name)+1);

JLI_StrCpy(splash_file_entry, SPLASH_FILE_ENV_ENTRY "=");

JLI_StrCat(splash_file_entry, splash_file_name);

putenv(splash_file_entry);

}

if (splash_jar_name && !headlessflag) {

char* splash_jar_entry = JLI_MemAlloc(JLI_StrLen(SPLASH_JAR_ENV_ENTRY "=")+JLI_StrLen(splash_jar_name)+1);

JLI_StrCpy(splash_jar_entry, SPLASH_JAR_ENV_ENTRY "=");

JLI_StrCat(splash_jar_entry, splash_jar_name);

putenv(splash_jar_entry);

}

/*

* The JRE-Version and JRE-Restrict-Search values (if any) from the

* manifest are overwritten by any specified on the command line.

*/

if (version != NULL)

info.jre_version = version;

if (restrict_search != -1)

info.jre_restrict_search = restrict_search;

/*

* "Valid" returns (other than unrecoverable errors) follow. Set

* main_class as a side-effect of this routine.

*/

if (info.main_class != NULL)

*main_class = JLI_StringDup(info.main_class);

/*

* If no version selection information is found either on the command

* line or in the manifest, simply return.

*/

if (info.jre_version == NULL) {

JLI_FreeManifest();

JLI_MemFree(new_argv);

return;

}

/*

* Check for correct syntax of the version specification (JSR 56).

*/

if (!JLI_ValidVersionString(info.jre_version)) {

JLI_ReportErrorMessage(SPC_ERROR1, info.jre_version);

exit(1);

}

/*

* Find the appropriate JVM on the system. Just to be as forgiving as

* possible, if the standard algorithms don't locate an appropriate

* jre, check to see if the one running will satisfy the requirements.

* This can happen on systems which haven't been set-up for multiple

* JRE support.

*/

jre = LocateJRE(&info);

JLI_TraceLauncher("JRE-Version = %s, JRE-Restrict-Search = %s Selected = %s\n",

(info.jre_version?info.jre_version:"null"),

(info.jre_restrict_search?"true":"false"), (jre?jre:"null"));

if (jre == NULL) {

if (JLI_AcceptableRelease(GetFullVersion(), info.jre_version)) {

JLI_FreeManifest();

JLI_MemFree(new_argv);

return;

} else {

JLI_ReportErrorMessage(CFG_ERROR4, info.jre_version);

exit(1);

}

}

/*

* If I'm not the chosen one, exec the chosen one. Returning from

* ExecJRE indicates that I am indeed the chosen one.

*

* The private environment variable _JAVA_VERSION_SET is used to

* prevent the chosen one from re-reading the manifest file and

* using the values found within to override the (potential) command

* line flags stripped from argv (because the target may not

* understand them). Passing the MainClass value is an optimization

* to avoid locating, expanding and parsing the manifest extra

* times.

*/

if (info.main_class != NULL) {

if (JLI_StrLen(info.main_class) <= MAXNAMELEN) {

(void)JLI_StrCat(env_entry, info.main_class);

} else {

JLI_ReportErrorMessage(CLS_ERROR5, MAXNAMELEN);

exit(1);

}

}

(void)putenv(env_entry);

ExecJRE(jre, new_argv);

JLI_FreeManifest();

JLI_MemFree(new_argv);

return;

}

逻辑也不复杂,大概就是,解析参数,读取manifest文件,jre版本校验,加载jre以便确认是否存在,最后将相关环境变量放置好。

4.2. 加载VM模块

加载VM是非常重要的一个工作。它是一个平台相关的实现,我们看下 windows版本的实现吧。

// share/windows/bin/java_md.c

/*

* Load a jvm from "jvmpath" and initialize the invocation functions.

*/

jboolean

LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn)

{

HINSTANCE handle;

JLI_TraceLauncher("JVM path is %s\n", jvmpath);

/*

* The Microsoft C Runtime Library needs to be loaded first. A copy is

* assumed to be present in the "JRE path" directory. If it is not found

* there (or "JRE path" fails to resolve), skip the explicit load and let

* nature take its course, which is likely to be a failure to execute.

*

*/

LoadMSVCRT();

// windows 中是通过路径加载dll文件实现

/* Load the Java VM DLL */

if ((handle = LoadLibrary(jvmpath)) == 0) {

JLI_ReportErrorMessage(DLL_ERROR4, (char *)jvmpath);

return JNI_FALSE;

}

/* Now get the function addresses */

// 获取虚拟机操作内存地址

ifn->CreateJavaVM =

(void *)GetProcAddress(handle, "JNI_CreateJavaVM");

ifn->GetDefaultJavaVMInitArgs =

(void *)GetProcAddress(handle, "JNI_GetDefaultJavaVMInitArgs");

if (ifn->CreateJavaVM == 0 || ifn->GetDefaultJavaVMInitArgs == 0) {

JLI_ReportErrorMessage(JNI_ERROR1, (char *)jvmpath);

return JNI_FALSE;

}

return JNI_TRUE;

}

可见,最重要的工作是被封装到 JRE 中的,应用层面只是调用JRE的方法即可。在windows中通过加载msvcrt模块完成工作,然后抽取vm的两个方法签名到ifn中,以便后续实用。

4.3. 解析参数信息

通过参数解析,我们就可以如何设置参数了。更深层次的理解。

// 实际就是语法规范

/*

* Parses command line arguments. Returns JNI_FALSE if launcher

* should exit without starting vm, returns JNI_TRUE if vm needs

* to be started to process given options. *pret (the launcher

* process return value) is set to 0 for a normal exit.

*/

static jboolean

ParseArguments(int *pargc, char ***pargv,

int *pmode, char **pwhat,

int *pret, const char *jrepath)

{

int argc = *pargc;

char **argv = *pargv;

int mode = LM_UNKNOWN;

char *arg;

*pret = 0;

while ((arg = *argv) != 0 && *arg == '-') {

argv++; --argc;

if (JLI_StrCmp(arg, "-classpath") == 0 || JLI_StrCmp(arg, "-cp") == 0) {

ARG_CHECK (argc, ARG_ERROR1, arg);

SetClassPath(*argv);

mode = LM_CLASS;

argv++; --argc;

} else if (JLI_StrCmp(arg, "-jar") == 0) {

ARG_CHECK (argc, ARG_ERROR2, arg);

mode = LM_JAR;

} else if (JLI_StrCmp(arg, "-help") == 0 ||

JLI_StrCmp(arg, "-h") == 0 ||

JLI_StrCmp(arg, "-?") == 0) {

printUsage = JNI_TRUE;

return JNI_TRUE;

} else if (JLI_StrCmp(arg, "-version") == 0) {

printVersion = JNI_TRUE;

return JNI_TRUE;

} else if (JLI_StrCmp(arg, "-showversion") == 0) {

showVersion = JNI_TRUE;

} else if (JLI_StrCmp(arg, "-X") == 0) {

printXUsage = JNI_TRUE;

return JNI_TRUE;

/*

* The following case checks for -XshowSettings OR -XshowSetting:SUBOPT.

* In the latter case, any SUBOPT value not recognized will default to "all"

*/

} else if (JLI_StrCmp(arg, "-XshowSettings") == 0 ||

JLI_StrCCmp(arg, "-XshowSettings:") == 0) {

showSettings = arg;

} else if (JLI_StrCmp(arg, "-Xdiag") == 0) {

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

/*

* The following case provide backward compatibility with old-style

* command line options.

*/

} else if (JLI_StrCmp(arg, "-fullversion") == 0) {

JLI_ReportMessage("%s full version \"%s\"", _launcher_name, GetFullVersion());

return JNI_FALSE;

} else if (JLI_StrCmp(arg, "-verbosegc") == 0) {

AddOption("-verbose:gc", NULL);

} else if (JLI_StrCmp(arg, "-t") == 0) {

AddOption("-Xt", NULL);

} else if (JLI_StrCmp(arg, "-tm") == 0) {

AddOption("-Xtm", NULL);

} else if (JLI_StrCmp(arg, "-debug") == 0) {

AddOption("-Xdebug", NULL);

} else if (JLI_StrCmp(arg, "-noclassgc") == 0) {

AddOption("-Xnoclassgc", NULL);

} else if (JLI_StrCmp(arg, "-Xfuture") == 0) {

AddOption("-Xverify:all", NULL);

} else if (JLI_StrCmp(arg, "-verify") == 0) {

AddOption("-Xverify:all", NULL);

} else if (JLI_StrCmp(arg, "-verifyremote") == 0) {

AddOption("-Xverify:remote", NULL);

} else if (JLI_StrCmp(arg, "-noverify") == 0) {

AddOption("-Xverify:none", NULL);

} else if (JLI_StrCCmp(arg, "-prof") == 0) {

char *p = arg + 5;

char *tmp = JLI_MemAlloc(JLI_StrLen(arg) + 50);

if (*p) {

sprintf(tmp, "-Xrunhprof:cpu=old,file=%s", p + 1);

} else {

sprintf(tmp, "-Xrunhprof:cpu=old,file=java.prof");

}

AddOption(tmp, NULL);

} else if (JLI_StrCCmp(arg, "-ss") == 0 ||

JLI_StrCCmp(arg, "-oss") == 0 ||

JLI_StrCCmp(arg, "-ms") == 0 ||

JLI_StrCCmp(arg, "-mx") == 0) {

char *tmp = JLI_MemAlloc(JLI_StrLen(arg) + 6);

sprintf(tmp, "-X%s", arg + 1); /* skip '-' */

AddOption(tmp, NULL);

} else if (JLI_StrCmp(arg, "-checksource") == 0 ||

JLI_StrCmp(arg, "-cs") == 0 ||

JLI_StrCmp(arg, "-noasyncgc") == 0) {

/* No longer supported */

JLI_ReportErrorMessage(ARG_WARN, arg);

} else if (JLI_StrCCmp(arg, "-version:") == 0 ||

JLI_StrCmp(arg, "-no-jre-restrict-search") == 0 ||

JLI_StrCmp(arg, "-jre-restrict-search") == 0 ||

JLI_StrCCmp(arg, "-splash:") == 0) {

; /* Ignore machine independent options already handled */

} else if (ProcessPlatformOption(arg)) {

; /* Processing of platform dependent options */

} else if (RemovableOption(arg)) {

; /* Do not pass option to vm. */

} else {

AddOption(arg, NULL);

}

}

if (--argc >= 0) {

*pwhat = *argv++;

}

if (*pwhat == NULL) {

*pret = 1;

} else if (mode == LM_UNKNOWN) {

/* default to LM_CLASS if -jar and -cp option are

* not specified */

mode = LM_CLASS;

}

if (argc >= 0) {

*pargc = argc;

*pargv = argv;

}

*pmode = mode;

return JNI_TRUE;

}

/*

* inject the -Dsun.java.command pseudo property into the args structure

* this pseudo property is used in the HotSpot VM to expose the

* Java class name and arguments to the main method to the VM. The

* HotSpot VM uses this pseudo property to store the Java class name

* (or jar file name) and the arguments to the class's main method

* to the instrumentation memory region. The sun.java.command pseudo

* property is not exported by HotSpot to the Java layer.

*/

void

SetJavaCommandLineProp(char *what, int argc, char **argv)

{

int i = 0;

size_t len = 0;

char* javaCommand = NULL;

char* dashDstr = "-Dsun.java.command=";

if (what == NULL) {

/* unexpected, one of these should be set. just return without

* setting the property

*/

return;

}

/* determine the amount of memory to allocate assuming

* the individual components will be space separated

*/

len = JLI_StrLen(what);

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

len += JLI_StrLen(argv[i]) + 1;

}

/* allocate the memory */

javaCommand = (char*) JLI_MemAlloc(len + JLI_StrLen(dashDstr) + 1);

/* build the -D string */

*javaCommand = '\0';

JLI_StrCat(javaCommand, dashDstr);

JLI_StrCat(javaCommand, what);

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

/* the components of the string are space separated. In

* the case of embedded white space, the relationship of

* the white space separated components to their true

* positional arguments will be ambiguous. This issue may

* be addressed in a future release.

*/

JLI_StrCat(javaCommand, " ");

JLI_StrCat(javaCommand, argv[i]);

}

AddOption(javaCommand, NULL);

}

// 设置 classpath

static void

SetClassPath(const char *s)

{

char *def;

const char *orig = s;

static const char format[] = "-Djava.class.path=%s";

/*

* usually we should not get a null pointer, but there are cases where

* we might just get one, in which case we simply ignore it, and let the

* caller deal with it

*/

if (s == NULL)

return;

s = JLI_WildcardExpandClasspath(s);

if (sizeof(format) - 2 + JLI_StrLen(s) < JLI_StrLen(s))

// s is corrupted after wildcard expansion

return;

def = JLI_MemAlloc(sizeof(format)

- 2 /* strlen("%s") */

+ JLI_StrLen(s));

sprintf(def, format, s);

AddOption(def, NULL);

if (s != orig)

JLI_MemFree((char *) s);

}

-Xxxxx, --xxx格式配置,如 -Xms1024G, --noclassgc ... 然后解析出来。最后通过AddOption()存储起来。

4.4. jvm初始化

好像我们一直讨论的都是这个,但是实际上里面还有一个真正的jvm的初始化过程。这里方才会接入真正的java程序,也才大家所关心的地方。

// java.c

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

}

/*

* Displays the splash screen according to the jar file name

* and image file names stored in environment variables

*/

void

ShowSplashScreen()

{

const char *jar_name = getenv(SPLASH_JAR_ENV_ENTRY);

const char *file_name = getenv(SPLASH_FILE_ENV_ENTRY);

int data_size;

void *image_data = NULL;

float scale_factor = 1;

char *scaled_splash_name = NULL;

if (file_name == NULL){

return;

}

scaled_splash_name = DoSplashGetScaledImageName(

jar_name, file_name, &scale_factor);

if (jar_name) {

if (scaled_splash_name) {

image_data = JLI_JarUnpackFile(

jar_name, scaled_splash_name, &data_size);

}

if (!image_data) {

scale_factor = 1;

image_data = JLI_JarUnpackFile(

jar_name, file_name, &data_size);

}

if (image_data) {

DoSplashInit();

DoSplashSetScaleFactor(scale_factor);

DoSplashLoadMemory(image_data, data_size);

JLI_MemFree(image_data);

}

} else {

DoSplashInit();

if (scaled_splash_name) {

DoSplashSetScaleFactor(scale_factor);

DoSplashLoadFile(scaled_splash_name);

} else {

DoSplashLoadFile(file_name);

}

}

if (scaled_splash_name) {

JLI_MemFree(scaled_splash_name);

}

DoSplashSetFileJarName(file_name, jar_name);

/*

* Done with all command line processing and potential re-execs so

* clean up the environment.

*/

(void)UnsetEnv(ENV_ENTRY);

(void)UnsetEnv(SPLASH_FILE_ENV_ENTRY);

(void)UnsetEnv(SPLASH_JAR_ENV_ENTRY);

JLI_MemFree(splash_jar_entry);

JLI_MemFree(splash_file_entry);

}

int

ContinueInNewThread(InvocationFunctions* ifn, jlong threadStackSize,

int argc, char **argv,

int mode, char *what, int ret)

{

/*

* If user doesn't specify stack size, check if VM has a preference.

* Note that HotSpot no longer supports JNI_VERSION_1_1 but it will

* return its default stack size through the init args structure.

*/

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;

}

}

{ /* Create a new thread to create JVM and invoke main method */

JavaMainArgs args;

int rslt;

args.argc = argc;

args.argv = argv;

args.mode = mode;

args.what = what;

args.ifn = *ifn;

rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);

/* If the caller has deemed there is an error we

* simply return that, otherwise we return the value of

* the callee

*/

return (ret != 0) ? ret : rslt;

}

}

看起来,jvm是通过一个新线程去运行应用系统的。在将执行控制权交由java代码后,它的主要作用,就是不停地接收命令,执行命令。从而变成一个真正的执行机器。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:送给她一个安稳的小窝(Python实现)第一讲:最能入门的爬虫教程(Python实现)第二讲:HTML基础(python)第三讲:爬虫——BeautifulSoup(Python)
下一篇:电力系统中随机矩阵理论的应用(Matlab实现)随机矩阵相关理论
相关文章

 发表评论

暂时没有评论,来抢沙发吧~