In depth understanding of jvm startup process of Java

  • 2021-08-17 00:11:58
  • OfStack

Directory 1. Talk about openjdk2. Talk about C language 3. Entry to openjdk 4. Startup flow of openjdk 4.0. Startup flow framework of jvm 4.1. Version selection process of jre 4.2. Load VM module 4.3. Parse parameter information 4.4. Initialization of jvm

jvm is the core operating platform of java, which is naturally a very complex system. Of course, to say that jvm is a platform is actually a general term. To be precise, it is a general name for an java virtual machine, and it does not refer to a specific virtual machine. Therefore, when talking about java virtual machines, we usually talk about something of a normative nature.

Then, if you want to study how jvm works, you can't talk in general terms. We must be specific to a specific virtual machine implementation in order to clarify its process.

1. Talk about openjdk

Because java is actually controlled by oracle, and oracle itself is a commercial company, java here is not completely open source to some extent. We call the official jdk oraclejdk. Or hotspot vm

At the same time, the community maintains a fully open source version, openjdk. In fact, most of these two jdk are the same, but the maintenance progress is not very 1, and the copyright ownership is not 1.

Therefore, if you want to study the implementation of jvm, it is a wise choice to do it based on openjdk.

If you want to know how openjdk is designed, what advanced features it has, and various best practices, then buying a book is the best choice.

If the industry has spare capacity and wants to know the source code, you can check the source code in official website. The source address of openjdk8 is: http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/because it is a foreign website, the speed will not be very fast. So just checking the source code on the website is still a little tired. In addition, without the help of ide, it is estimated that few people can stick to it. Another download address, you can search on the Internet, resources are always available, and Chinese links are fast. Spend more time looking for it.

Of course, one point to be explained is: 1 source code reading without design background and framework concept is all rogue. That kind of work is like a castle in the air, which is not practical.

2. Talk about the C language

C language, 1 as our university entry language, more or less have been exposed. But to say proficient, there may be very few people. But what I want to say is, as long as you have learned C language, it is basically not a problem for most program reading.

In the implementation of openjdk, the first part of its core is written in C language, and of course many other languages are also like 1. Therefore, C language is very important in the underlying world. This is just to say that it is important, but it does not mean that it is the most powerful, that is, GG, which is not written in C language, is better than JJ, which is written in JAVA. Because, regardless of work, language is the same. It's just that each has its own strengths. The point is not here, it is thought.

C language programming several major processes: write code (the core), compile, link (the most troublesome), run.

Of course, the core of nature is to write code. No, the core is to do design.

In C language, one main () function is used as the entry. After writing various logics, various complex logics are realized by calling and controlling main () method.

Therefore, to study a project, the first thing is to find its entrance. Then, according to the purpose, the path learning of each function realization is carried out.

C language has extremely flexible syntax, super complex pointer design, structure similar to object-oriented idea, and the ability to obtain information from operating system at any time (various links). Therefore, C language is sometimes difficult to read. There is no way to do this. It will be easy, but it will be difficult. This is an unchanging truth. It is a multiple-choice question and an application question.

One sentence, one o'clock, is enough for people who eat melons.

3. Entrance to openjdk

As mentioned above, to study an C project, the first thing is to find its entrance. So, where is the entrance to openjdk?

In share/bin/main. c, the main () method is its entry. The name of this file is clear enough. A discerning person will know when he looks at it. Haha, but generally, we still need to look up the information to know.

main. c is the only 1main method entry of jvm. After jdk is compiled, there will be many work boxes, such as jmap, jps, jstack... The entry of these toolboxes is actually this main, but they contain different sub-modules, so as to achieve the purpose of different tools.

main. c content is not much, mainly it is only a framework, in order to shield the differences of various systems. It exists mainly to introduce the JLI_LAUNCH () method, which is equivalent to defining its own main () method.


/*
 * 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 <crtassem.h>
#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.
 */
//  Define the entry function, JAVAW Use in mode  WinMain(),  Otherwise use  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 Obtain parameters under 
 {
 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 */
 //  Various linux Parameters on the platform, taken directly from main Insert parameters 
 margc = argc;
 margv = argv;
#endif /* WIN32 */
 //  Core :  Redefine the entry method to : 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);
}

Because java is designed to be a cross-platform language, how can it be cross-platform? Because platform differences always exist, if the language itself does not pay attention to the platform, then naturally someone pays attention to the platform behind it, thus shielding the differences. Yes, this is the meaning of virtual machines. Therefore, in the entrance method, we can see that it pays attention to platform differences when it comes up. This is a must.

4. openjdk startup process

With the above entrance knowledge, it seems that I have understood some truth. However, it still seems that the purpose of understanding the startup process has not been achieved. Don't worry, listen to me slowly.

When we start a virtual machine, we usually use java-classpath: xxx < other-options > xx. xx, or java-jar < other-options > xx. jar. It doesn't matter how to use it. The point is that we are all virtual machines started by java. Therefore, we know that the java program is the core switch for us to start jvm.

4.0. jvm Startup Process Framework

Needless to say, java. c is an important document for us to study. It will be a superman to control the implementation of the startup process. And the entrance to it is the definition JLI_Launch (...) in main (), so let's take a look at it.


// 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;
 //  Initialize starter 
 InitLauncher(javaw);
 //  Print status 
 DumpState();
 //  Trace call start 
 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.)
 */
 //  Parse command line arguments, select 1jre Version 
 SelectVersion(argc, argv, &main_class);
 CreateExecutionEnvironment(&argc, &argv,
  jrepath, sizeof(jrepath),
  jvmpath, sizeof(jvmpath),
  jvmcfg, sizeof(jvmcfg));
 if (!IsJavaArgs()) {
 //  Settings 1 Some special environment variables 
 SetJvmEnvironment(argc,argv);
 }
 ifn.CreateJavaVM = 0;
 ifn.GetDefaultJavaVMInitArgs = 0;
 if (JLI_IsTraceLauncher()) {
 start = CounterGet(); //  Record startup time 
 }
 //  Loading VM,  Top priority 
 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;
 //  Parse more parameter information 
 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.
 */
 //  Analytic parameter 
 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();
 //  Initialization jvm That is, loading java When the program starts, apply the performance time to 
 return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
}

The above is the startup process framework of the whole jvm virtual machine, which basically can't run away from a few points, that is, parsing command line parameters and setting parameters to a certain range or environment variables. Load necessary modules and pass variable storage. Initialize the system. Parse the user system implementation. Of course, in general, the main loop of the system will be realized. This action is completed by using the system, and jvm is only responsible for execution.

Because we just want to know about it, we don't think so, but any one of them is enough to study for a long time. Put that aside, pick up a sesame seed first. Need to understand: I know a lot of truth but still can't live this life well. I can only be a melon eater with peace of mind.

Below, on a few details, we can look at interest and have a little in-depth understanding!

4.1. jre Version Selection Process

In the above framework, we can further refine several important nodes. Don't say the details, it's too complicated. First of all, it is very important to determine the jre version used by the current system, which determines whether the application system can run or not. Because sometimes the user of the system is not a developer, there must be a correct version of jre. Without an jre environment, all java executions would be empty talk.


// java.c
/*
 * The SelectVersion() routine ensures that an appropriate version of
 * the JRE is running. The specification 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;
}

The logic is not complicated, that is, parsing parameters, reading manifest file, verifying jre version, loading jre to confirm whether it exists, and finally placing relevant environment variables.

4.2. Load the VM module

Loading VM is a very important job. It is a platform-related implementation, let's look at the implementation of windows version.


// 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  Is loaded by path in dll File implementation 
 /* Load the Java VM DLL */
 if ((handle = LoadLibrary(jvmpath)) == 0) {
 JLI_ReportErrorMessage(DLL_ERROR4, (char *)jvmpath);
 return JNI_FALSE;
 }
 /* Now get the function addresses */
 //  Get the virtual machine operation memory address 
 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;
}

It can be seen that the most important work is encapsulated in JRE, and the application level only calls the methods of JRE. In windows, the work is completed by loading msvcrt module, and then two method signatures of vm are extracted into ifn for subsequent application.

4.3. Parsing Parameter Information

Through parameter parsing, we can set the parameters. A deeper understanding.


//  Actually, it is grammatical norms 
/*
 * 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);
}

//  Settings  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 format configuration, such as-Xms 1024G,--noclassgc... and then parsed out. Finally, it is stored through AddOption ().

4.4. jvm initialization

It seems that this is all we have been talking about, but there is actually a real initialization process for jvm. The real java program will be connected here, which is what everyone cares about.


// 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;
 }
}

It seems that jvm runs the application system through a new thread. After the execution control is handed over to java code, its main function is to continuously receive and execute commands. So as to become a real execution machine.


Related articles: