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)