Uncover the essential logic of java commands

preface

In daily coding, with the support of ide, we rarely execute java XXX command directly on the command line to start a project. However, have we ever thought about what is done behind a simple java command? Let's look at the following simple questions

1. The Java command can be followed by many parameters. How are these parameters resolved? Why does - version return the version number and start the jvm if it is followed by a class name?

2. Why must our own defined entry method meet the following signatures? Are there other possibilities?

public static void main(String[] args) {
}

3. If we need to call the native method written by ourselves, we must explicitly pass system Loadlibrary() loads the dynamic link library. If we look at the basic classes of java (Thread, Object, Class, etc.), there are many native methods in these classes, we will find that there is no internal call to system Instead, registerNatives() in the static constructor is responsible for registering other natvie methods.

For example: thread java

class Thread implements Runnable {
    private static native void registerNatives();
    static {
        registerNatives();
    }
    ...
}

However, registerNatives() itself is also a native method. When was its dynamic link library loaded?

There is no need to say more about question 1 and question 2. The answer must be in the java command

For question 3, because Thread, Object, Class, etc. are the native classes of jdk, and their related dynamic link library is the JVM itself (jvm.dll for windows system, libjvm.so for linux system and libjvm.dylib for mac system), it is easy to speculate that the process of loading dynamic link library must be in the startup process of JVM.

Today, we will take the above three questions as the introduction to explore the essence behind java commands, that is, the starting process of jvm

Analysis of jvm startup process

Since the jvm startup process needs to be analyzed, the source code of jdk and hotspot is indispensable. Download address: http://hg.openjdk.java.net/jdk8

Main entrance method

View Java c. jdk directory / SRC / Java Base / share / native / libjli, the directory will be different due to different versions of jdk

The entry method is JLI_Launch, of course, has a lot of content. Let's choose the key parts

int
JLI_Launch(args)
{
  ...
  //Execution environment creation
  CreateExecutionEnvironment(&argc, &argv,
                               jrepath, sizeof(jrepath),
                               jvmpath, sizeof(jvmpath),
                               jvmcfg,  sizeof(jvmcfg));
  ...
  //Load jvm
  if (!LoadJavaVM(jvmpath, &ifn)) {
        return(6);
  }
  ...
  //Parse command line parameters, such as - h, - version, and so on
  if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath))
  {
        return(ret);
  }
  ...
  //Start the jvm
  return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
}

Then, let's look at the logic of these main methods

CreateExecutionEnvironment: creates an execution environment

This method has different logic according to different operating systems. Take linux system as an example

View java_md_solinux.c. jdk directory / SRC / Java base/unix/native/libjli

CreateExecutionEnvironment(args) {
    /**
     * Get the path of jre
     */
    if (!GetJREPath(jrepath, so_jrepath, JNI_FALSE) ) {
        JLI_ReportErrorMessage(JRE_ERROR1);
        exit(2);
    }
    JLI_Snprintf(jvmcfg, so_jvmcfg, "%s%slib%s%sjvm.cfg",
                    jrepath, FILESEP, FILESEP, FILESEP);
    /**
     * Read the version of the jvm. Here, find the jvm according to the path of the jre Cfg file
     */
    if (ReadKnownVMs(jvmcfg, JNI_FALSE) < 1) {
        JLI_ReportErrorMessage(CFG_ERROR7);
        exit(1);
    }

    jvmpath[0] = '\0';
    /**
     * Check the jvm version. If it is specified on the command line, the specified jvm version will be used. Otherwise, the default version will be used
     */
    jvmtype = CheckJvmType(pargc, pargv, JNI_FALSE);
    if (JLI_StrCmp(jvmtype, "ERROR") == 0) {
        JLI_ReportErrorMessage(CFG_ERROR9);
        exit(4);
    }
    /**
     * Get the path of dynamic link library
     */
    if (!GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath, 0 )) {
        JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath);
        exit(4);
    }
}

There are mainly the following four steps

1. Determine the path of jre

Priority will be given to finding the current directory of the application

if (GetApplicationHome(path, pathsize)) {
    ...
}

if (GetApplicationHomeFromDll(path, pathsize)) {
    ...
}

2. Splice the jvm according to jre CFG and read the available jvm configurations

General JVM The cfg file is in / jre/lib, and its contents are as follows:

-server KNOWN
-client IGNORE

The above two lines of configuration correspond to different jvm versions respectively. For example, the first line - server know. When loading the jvm dynamic link library, you will look in the / jre/lib/server directory

3. Check jvm type

When executing java commands, you can specify the jvm version through the command. If it is not specified, then the jvm First jvm version in CFG

i = KnownVMIndex(arg);
if (i >= 0) {
    ...
}
else if (JLI_StrCCmp(arg, "-XXaltjvm=") == 0 || JLI_StrCCmp(arg, "-J-XXaltjvm=") == 0) {
    ...
}

4. Get the path of dynamic link library

According to the results of checking the jvm type, get the path of the corresponding jvm dynamic link library. By default, the lib path obtained in the Mac system is as follows

The server in the path is the - server read in the cfg file earlier

/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/server/libjvm.dylib

LoadJavaVM: load jvm

View java_md_solinux.c. jdk directory / SRC / Java base/unix/native/libjli

jboolean
LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn)
{   
    /**
     * Load the dynamic link library. Here, dlopen is called instead of ordinary open
     */
    libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
    ...
    /**
     * Link the "JNI_CreateJavaVM" method in the jvm to the CreateJavaVM method of the jdk
     */
    ifn->CreateJavaVM = (CreateJavaVM_t)
        dlsym(libjvm, "JNI_CreateJavaVM");    
    /**
     * Call CreateJavaVM method
     */
    if (ifn->CreateJavaVM == NULL) {
        JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
        return JNI_FALSE;
    }
		/**
     * Link the "JNI_GetDefaultJavaVMInitArgs" method in the jvm to the GetDefaultJavaVMInitArgs method in the jdk
     */
    ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t)
        dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs");  
    /**
     * Call the GetDefaultJavaVMInitArgs method
     */
    if (ifn->GetDefaultJavaVMInitArgs == NULL) {
        JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
        return JNI_FALSE;
    }
		/**
     * Link the "JNI_GetCreatedJavaVMs" method in the jvm to the GetCreatedJavaVMs method of the jdk
     */
    ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t)
        dlsym(libjvm, "JNI_GetCreatedJavaVMs");  
    /**
     * Call GetCreatedJavaVMs method
     */
    if (ifn->GetCreatedJavaVMs == NULL) {
        JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
        return JNI_FALSE;
    }
}

The main steps are as follows:

1. Loading dynamic link library is the answer to our first question

Dlopen method is the abbreviation of dynamic link open. It loads the dynamic link library while opening the file. You can view the description through the man dlopen command

man dlopen
dlopen -- load and link a dynamic library or bundle

2. Link and call JNI in jvm_ CreateJavaVM ,GetDefaultJavaVMInitArgs,GetCreatedJavaVMs

dlsym method is the abbreviation of dynamic link symbol, which links the method in the dynamic link library to the current method

man dlsym
dlsym -- get address of a symbol

As the name suggests, these three methods are to create a jvm, obtain the default jvm startup parameters, and obtain the created jvm. The entrance of these three methods is

hotspot directory / SRC / share / VM / prims / JNI cpp

In the file, interested students can view it by themselves

ParseArguments: parse command line arguments

View Java c. jdk directory / SRC / Java base/share/native/libjli

static jboolean
ParseArguments(int *pargc, char ***pargv,
               int *pmode, char **pwhat,
               int *pret, const char *jrepath)
{
  ...
  if (JLI_StrCmp(arg, "--version") == 0) {
      printVersion = JNI_TRUE;
      printTo = USE_STDOUT;
      return JNI_TRUE;
  }
  ...
  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);
  }
  ...
}

There are two categories of parameters.

1. Parameters similar to -- version will be returned directly after parsing

2. Parameters similar to - mx and - mx will be added as VM option through AddOption method

/*
 * Adds a new VM option with the given name and value.
 */
void
AddOption(char *str, void *info)
{
  ...
}

JVMInit: start the jvm

View java_md_solinux.c. jdk directory / SRC / Java base/unix/native/libjli

JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
        int argc, char **argv,
        int mode, char *what, int ret)
{
    //Start the jvm in a new thread
    return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
}

In this method, ContinueInNewThread is called to create a new thread and start the jvm

View Java c. jdk directory / SRC / Java base/share/native/libjli

int
ContinueInNewThread(InvocationFunctions* ifn, jlong threadStackSize,
                    int argc, char **argv,
                    int mode, char *what, int ret)
{
  ...
  /**
   * Create a new thread, create the jvm and call the main method
   */
  rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);
  return (ret != 0) ? ret : rslt;
}

In this method, ContinueInNewThread0 will be called and the JavaMain entry method will be passed in

View java_md_solinux.c. jdk directory / SRC / Java base/unix/native/libjli

/**
 * Block the current thread and execute the main method in a new thread
 */
int
ContinueInNewThread0(int (JNICALL *continuation)(void *), jlong stack_size, void * args) {
    //Create a new thread to execute the incoming continuation, which is actually the main method passed in from outside
    if (pthread_create(&tid, &attr, (void *(*)(void*))continuation, (void*)args) == 0) {
      void * tmp;
      //Current thread blocking
      pthread_join(tid, &tmp);
      rslt = (int)(intptr_t)tmp;
    }
    ...
}

In this method, a new thread is created to call the incoming main method, while the current thread is blocked

Because here pthread_join is waiting on the thread running the main method, so when the java program runs, if the main thread ends, the whole process will end, and the sub thread started by main has no impact on the whole process

View Java c. jdk directory / SRC / Java base/share/native/libjli

int JNICALL
JavaMain(void * _args)
{
  //Start the jvm
  if (!InitializeJVM(&vm, &env, &ifn)) {
      JLI_ReportErrorMessage(JVM_ERROR1);
      exit(1);
  }
  ...
  //Load main class
  mainClass = LoadMainClass(env, mode, what);
  //Find main method id
  mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                       "([Ljava/lang/String;)V");
  //Callback the main method in java code through jni
  (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
}

Here, the method name and signature of the main method are fixed. Therefore, no matter what java program is, the entry method must be public static void main(String[] args)

At this point, the code flow of the jvm from preparing to start to finally executing the main method is over. Because the method of this process is scattered in different documents, it will make people dizzy, so I summarized it into the following structure for everyone to understand

Entrance method: JLI_Launch

        |--------->Create execution environment: CreateExecutionEnvironment

        |          |--------->obtain jre Path to: GetJREPath

        |          |--------->read jvm to configure: ReadKnownVMs

        |          |--------->inspect jvm Type: CheckJvmType

        |          |--------->obtain jvm Dynamic link library path: GetJVMPath 

        |--------->load jvm Dynamic link library: LoadJavaVM

        |          |--------->Load dynamic link library: dlopen

        |          |--------->link jvm method: dlsym

        |--------->Parse command line parameters: ParseArguments

        |          |--------->be similar to --version The parameters of will be returned directly after parsing

        |          |--------->be similar to -mx,-mx The parameters of the are passed AddOption Method added as VM option

        |--------->start-up jvm And execute main method: JVMInit       

                   |--------->Create a new thread and perform subsequent tasks: ContinueInNewThread

                               |--------->Create new thread execution main method: ContinueInNewThread0(JavaMain)

                                 			|--------->Create a new thread to execute the incoming main method: pthread_create

                                 			|--------->Block current thread: pthread_join

                               |--------->obtain main method: JavaMain

                                 			|--------->Load main class: LoadMainClass

                                 			|--------->Get by signature main Method of id: GetStaticMethodID

                                 			|--------->implement main method: CallStaticVoidMethod

last

As a passer-by, Xiaobian has sorted out a lot of advanced architecture video materials, interview documents and PDF learning materials. For the above set of system outline, Xiaobian also has corresponding advanced architecture video materials, such as' determining your future path or learning to improve your technology stack Partners with technical knowledge can click here to get free learning materials to improve themselves (full set of interview documents, PDF, advanced architecture video)

 

Keywords: Java Interview

Added by uidzer0b on Tue, 08 Feb 2022 02:12:45 +0200