java.base/share/native/launcher/main.c方法通过对系统参数的处理,得到不同平台版本的参数,传递进入JLI_Launch
实现方法在java.c当中
JNIEXPORT int JNICALL
JLI_Launch(int argc, char ** argv, /* main argc, argv */
int jargc, const char** jargv, /* java args */
int appclassc, const char** appclassv, /* app classpath */
const char* fullversion, /* full version defined */
const char* dotversion, /* UNUSED 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 /* unused */
)
{
int mode = LM_UNKNOWN;
char *what = NULL;
char *main_class = NULL;
int ret;
InvocationFunctions ifn;
jlong start = 0, end = 0;
char jvmpath[MAXPATHLEN];
char jrepath[MAXPATHLEN];
char jvmcfg[MAXPATHLEN];
_fVersion = fullversion;
_launcher_name = lname;
_program_name = pname;
_is_java_args = javaargs;
_wc_enabled = cpwildcard;
//从此处java处理参数以及开启debug打印系统参数
InitLauncher(javaw);
DumpState();
if (JLI_IsTraceLauncher()) {
int i;
printf("Java args:\n");
for (i = 0; i < jargc ; i++) {
printf("jargv[%d] = %s\n", i, jargv[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);
}
//在c的环境变量中获取启动类的名称到 main_class,解析参数判断jarflag,如果时jar还需要读取manifest来确定main_class
SelectVersion(argc, argv, &main_class);
//从启动参数中获取JRE,检查jvm.cfg默认jvm配置(描述当前虚拟机类型是server或者client)等配置,随后再找jvm.dll(linux找libjvm.so)路径
CreateExecutionEnvironment(&argc, &argv,
jrepath, sizeof(jrepath),
jvmpath, sizeof(jvmpath),
jvmcfg, sizeof(jvmcfg));
ifn.CreateJavaVM = 0;
ifn.GetDefaultJavaVMInitArgs = 0;
if (JLI_IsTraceLauncher()) {
start = CurrentTimeMicros();
}
//dlsym 动态加载jvm库文件启动虚拟机
if (!LoadJavaVM(jvmpath, &ifn)) {
return(6);
}
if (JLI_IsTraceLauncher()) {
end = CurrentTimeMicros();
}
JLI_TraceLauncher("%ld micro seconds to LoadJavaVM\n", (long)(end-start));
++argv;
--argc;
if (IsJavaArgs()) {
/* Preprocess wrapper arguments */
TranslateApplicationArgs(jargc, jargv, &argc, &argv);
if (!AddApplicationOptions(appclassc, appclassv)) {
return(1);
}
} else {
/* Set default CLASSPATH */
char* cpath = getenv("CLASSPATH");
if (cpath != NULL) {
//设置需要加载的运行路径解析到-Djava.class.path后面,同时设置到了vm options当中
SetClassPath(cpath);
}
}
/* Parse command line options; if the return value of ParseArguments is false, the program should exit.
处理参数如verbosegc,XshowSettings,--class-path等,部分参数如-Xdebug需要添加到vm option
*/
if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath)) {
return(ret);
}
/* Override class path if -jar flag was specified */
if (mode == LM_JAR) {
//jar包的classpath重新设置
SetClassPath(what); /* Override class path */
}
/* set the -Dsun.java.command pseudo property */
SetJavaCommandLineProp(what, argc, argv);
/* Set the -Dsun.java.launcher pseudo property */
SetJavaLauncherProp();
return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
}
Linux JVMInit最后调用到的方法,在新线程中运行jvm和main入口主函数
int
CallJavaMainInNewThread(jlong stack_size, void* args) {
int rslt;
pthread_t tid;
pthread_attr_t attr;
//初始化一个线程的对象属性,是调用linux的系统函数
pthread_attr_init(&attr);
//修改线程状态,是可以被Jion的,使用完就必须手动销毁,可分离的就能自动
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
size_t adjusted_stack_size;
//设置堆的大小
if (stack_size > 0) {
if (pthread_attr_setstacksize(&attr, stack_size) == EINVAL) {
// System may require stack size to be multiple of page size
// Retry with adjusted value
adjusted_stack_size = adjustStackSize(stack_size);
if (adjusted_stack_size != (size_t) stack_size) {
pthread_attr_setstacksize(&attr, adjusted_stack_size);
}
}
}
//设置线程保护区大小
pthread_attr_setguardsize(&attr, 0); // no pthread guard page on java threads
//线程创建,ThreadJavaMain在线程中运行启动主函数,启动我们的框架
if (pthread_create(&tid, &attr, ThreadJavaMain, args) == 0) {
void* tmp;
//当前线程阻塞等待线程执行完毕
pthread_join(tid, &tmp);
rslt = (int)(intptr_t)tmp;
} else {
/*
* Continue execution in current thread if for some reason (e.g. out of
* memory/LWP) a new thread can't be created. This will likely fail
* later in JavaMain as JNI_CreateJavaVM needs to create quite a
* few new threads, anyway, just give it a try..
* 线程创建失败,直接在进程中执行代码
*/
rslt = JavaMain(args);
}
pthread_attr_destroy(&attr);
return rslt;
}
java main才开始真正创建jvm实例,前面都是准备工作
LoadMainClass这里可以特别注意一下,java项目比如springboot jar包就是在这里加载了主函数,通过LauncherHelper的MAIN_CLASS解析jar包触发boot项目加载
int
JavaMain(void* _args)
{
...
RegisterThread();
/* Initialize the virtual machine 所有配置已经解析完成才实例化虚拟机 */
start = CurrentTimeMicros();
if (!InitializeJVM(&vm, &env, &ifn)) {
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
...
/* If the user specified neither a class name nor a JAR file */
if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
PrintUsage(env, printXUsage);
CHECK_EXCEPTION_LEAVE(1);
LEAVE();
}
//在前面读取cfg文件时获取到不同的vm类型到knownVMs,就是一个记录
FreeKnownVMs(); /* after last possible PrintUsage */
if (JLI_IsTraceLauncher()) {
end = CurrentTimeMicros();
JLI_TraceLauncher("%ld micro seconds to InitializeJVM\n", (long)(end-start));
}
/* At this stage, argc/argv have the application's arguments */
if (JLI_IsTraceLauncher()){
int i;
printf("%s is '%s'\n", launchModeNames[mode], what);
printf("App's argc is %d\n", argc);
for (i=0; i < argc; i++) {
printf(" argv[%2d] = '%s'\n", i, argv[i]);
}
}
ret = 1;
//获取主函数启动类,java启动的单个类的main函数或者其他框架的jarlaucher,又或者tomcat的startup.Bootstrap类
mainClass = LoadMainClass(env, mode, what);
CHECK_EXCEPTION_NULL_LEAVE(mainClass);
appClass = GetApplicationClass(env);
NULL_CHECK_RETURN_VALUE(appClass, -1);
mainArgs = CreateApplicationArgs(env, argv, argc);
CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
if (dryRun) {
ret = 0;
LEAVE();
}
/*
* PostJVMInit uses the class name as the application name for GUI purposes,
* for example, on OSX this sets the application name in the menu bar for
* both SWT and JavaFX. So we'll pass the actual application class here
* instead of mainClass as that may be a launcher or helper class instead
* of the application class.
*/
PostJVMInit(env, appClass, vm);
CHECK_EXCEPTION_LEAVE(1);
/*
* The LoadMainClass not only loads the main class, it will also ensure
* that the main method's signature is correct, therefore further checking
* is not required. The main method is invoked here so that extraneous java
* stacks are not in the application stack trace.
*/
#define MAIN_WITHOUT_ARGS 1
#define MAIN_NONSTATIC 2
//获取 sun.launcher.LauncherHelper
jclass helperClass = GetLauncherHelperClass(env);
//通过Jni调用上面的getMainType
jmethodID getMainType = (*env)->GetStaticMethodID(env, helperClass,
"getMainType",
"()I");
CHECK_EXCEPTION_NULL_LEAVE(getMainType);
int mainType = (*env)->CallStaticIntMethod(env, helperClass, getMainType);
CHECK_EXCEPTION_LEAVE(mainType);
//通过jni调用入口类的主方法,系统就开始启动了,直到执行完毕退出或者进程中止
switch (mainType) {
case 0: {
mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
"([Ljava/lang/String;)V");
CHECK_EXCEPTION_NULL_LEAVE(mainID);
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
break;
}
case MAIN_WITHOUT_ARGS: {
mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
"()V");
CHECK_EXCEPTION_NULL_LEAVE(mainID);
(*env)->CallStaticVoidMethod(env, mainClass, mainID);
break;
}
case MAIN_NONSTATIC: {
constructor = (*env)->GetMethodID(env, mainClass, "<init>", "()V");
CHECK_EXCEPTION_NULL_LEAVE(constructor);
mainObject = (*env)->NewObject(env, mainClass, constructor);
CHECK_EXCEPTION_NULL_LEAVE(mainObject);
mainID = (*env)->GetMethodID(env, mainClass, "main",
"([Ljava/lang/String;)V");
CHECK_EXCEPTION_NULL_LEAVE(mainID);
(*env)->CallVoidMethod(env, mainObject, mainID, mainArgs);
break;
}
case MAIN_NONSTATIC | MAIN_WITHOUT_ARGS: {
constructor = (*env)->GetMethodID(env, mainClass, "<init>", "()V");
CHECK_EXCEPTION_NULL_LEAVE(constructor);
mainObject = (*env)->NewObject(env, mainClass, constructor);
CHECK_EXCEPTION_NULL_LEAVE(mainObject);
mainID = (*env)->GetMethodID(env, mainClass, "main",
"()V");
CHECK_EXCEPTION_NULL_LEAVE(mainID);
(*env)->CallVoidMethod(env, mainObject, mainID);
break;
}
}
/*
* The launcher's exit code (in the absence of calls to
* System.exit) will be non-zero if main threw an exception.
*/
ret = (*env)->ExceptionOccurred(env) == NULL 0 : 1;
LEAVE();
}
CallStaticVoidMethod
在jni.cpp当中
JNI_ENTRY(void, jni_CallStaticVoidMethod(JNIEnv *env, jclass cls, jmethodID methodID, ...))
HOTSPOT_JNI_CALLSTATICVOIDMETHOD_ENTRY(env, cls, (uintptr_t) methodID);
DT_VOID_RETURN_MARK(CallStaticVoidMethod);
va_list args;
va_start(args, methodID);
JavaValue jvalue(T_VOID);
JNI_ArgumentPusherVaArg ap(methodID, args);
jni_invoke_static(env, &jvalue, nullptr, JNI_STATIC, methodID, &ap, CHECK);
va_end(args);
JNI_END
编译后代码大概长这样
extern "C" {
void JNICALL jni_CallStaticVoidMethod(JNIEnv *env, jclass cls, jmethodID methodID, ...){
JavaThread* thread=JavaThread::thread_from_jni_environment(env);
assert(thread == Thread::current(), "JNIEnv is only valid in same thread");
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread));
ThreadInVMfromNative __tiv(thread);
debug_only(VMNativeEntryWrapper __vew;)
HandleMarkCleaner __hm(thread); \
JavaThread* THREAD = thread; /* For exception macros. */ \
os::verify_stack_alignment();
va_list args;
va_start(args, methodID);
JavaValue jvalue(T_VOID);
JNI_ArgumentPusherVaArg ap(methodID, args);
jni_invoke_static(env, &jvalue, nullptr, JNI_STATIC, methodID, &ap, CHECK);
va_end(args);
}}
其中JNICALL是一个空定义,只做标志,主体调用仍然是 jni_invoke_static
static void jni_invoke_static(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) {
methodHandle method(THREAD, Method::resolve_jmethod_id(method_id));
// Create object to hold arguments for the JavaCall, and associate it with
// the jni parser
ResourceMark rm(THREAD);
int number_of_parameters = method->size_of_parameters();
JavaCallArguments java_args(number_of_parameters);
assert(method->is_static(), "method should be static");
// Fill out JavaCallArguments object
args->push_arguments_on(&java_args);
// Initialize result type
result->set_type(args->return_type());
// Invoke the method. Result is returned as oop.
JavaCalls::call(result, method, &java_args, CHECK);
// Convert result
if (is_reference_type(result->get_type())) {
result->set_jobject(JNIHandles::make_local(THREAD, result->get_oop()));
}
}
JavaCalls::call(result, method, &java_args, CHECK);
这是java执行方法的地方
其执行的方法void JavaCalls::call_helper(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS)
可以找到方法
StubRoutines::call_stub()(
(address)&link,
// (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
result_val_address, // see NOTE above (compiler problem)
result_type,
method(),
entry_point,
parameter_address,
args->size_of_parameters(),
CHECK
);
通过查看StubRoutines类的定义可以看到实际调用的方法还是交给了StubGenerator
class StubRoutines: AllStatic {
public:
//这个友元类StubGenerator能够访问StubRoutines内的变量,然后根据jdk版本不同,针对不同系统有不同实现
friend class StubGenerator;
#include CPU_HEADER(stubRoutines)
static jint _verify_oop_count;
static address _verify_oop_subroutine_entry;
static address _call_stub_return_address; // the return PC, when returning to a call stub
//call_stub()这里调用
static address _call_stub_entry;
//……省略其他代码
typedef void (*CallStub)(
address link,
intptr_t* result,
BasicType result_type,
Method* method,
address entry_point,
intptr_t* parameters,
int size_of_parameters,
TRAPS
);
//调用_call_stub_entry,也就是说方法最后调用的是地址
static CallStub call_stub() { return CAST_TO_FN_PTR(CallStub, _call_stub_entry); }
}
选取openjdk-master\src\hotspot\cpu\x86\stubGenerator_x86_64.cpp这一个实现类生成了代码指令
address start = __ pc();
方法调用指令前获取到了当前的pc寄存器位置,方法调用完成后返回了此地址
具体的实现在MacroAssembler,这个类通过
MacroAssembler* _masm = new MacroAssembler(&code);
将代码翻译为cpu的汇编指令进行执行
此处调用java方法,主函数在此启动后进行后续处理
//_call_stub_entry 实际地址在这里被创建
StubRoutines::_call_stub_entry =
generate_call_stub(StubRoutines::_call_stub_return_address);
address StubGenerator::generate_call_stub(address& return_address) {
assert((int)frame::entry_frame_after_call_words == -(int)rsp_after_call_off + 1 &&
(int)frame::entry_frame_call_wrapper_offset == (int)call_wrapper_off,
"adjust this code");
StubCodeMark mark(this, "StubRoutines", "call_stub");
address start = __ pc();
// same as in generate_catch_exception()!
const Address rsp_after_call(rbp, rsp_after_call_off * wordSize);
const Address call_wrapper (rbp, call_wrapper_off * wordSize);
const Address result (rbp, result_off * wordSize);
const Address result_type (rbp, result_type_off * wordSize);
const Address method (rbp, method_off * wordSize);
const Address entry_point (rbp, entry_point_off * wordSize);
const Address parameters (rbp, parameters_off * wordSize);
const Address parameter_size(rbp, parameter_size_off * wordSize);
// same as in generate_catch_exception()!
const Address thread (rbp, thread_off * wordSize);
const Address r15_save(rbp, r15_off * wordSize);
const Address r14_save(rbp, r14_off * wordSize);
const Address r13_save(rbp, r13_off * wordSize);
const Address r12_save(rbp, r12_off * wordSize);
const Address rbx_save(rbp, rbx_off * wordSize);
// stub code
__ enter();
__ subptr(rsp, -rsp_after_call_off * wordSize);
// save register parameters
#ifndef _WIN64
__ movptr(parameters, c_rarg5); // parameters
__ movptr(entry_point, c_rarg4); // entry_point
#endif
__ movptr(method, c_rarg3); // method
__ movl(result_type, c_rarg2); // result type
__ movptr(result, c_rarg1); // result
__ movptr(call_wrapper, c_rarg0); // call wrapper
// save regs belonging to calling function
__ movptr(rbx_save, rbx);
__ movptr(r12_save, r12);
__ movptr(r13_save, r13);
__ movptr(r14_save, r14);
__ movptr(r15_save, r15);
#ifdef _WIN64
int last_reg = 15;
if (UseAVX > 2) {
last_reg = 31;
}
if (VM_Version::supports_evex()) {
for (int i = xmm_save_first; i <= last_reg; i++) {
__ vextractf32x4(xmm_save(i), as_XMMRegister(i), 0);
}
} else {
for (int i = xmm_save_first; i <= last_reg; i++) {
__ movdqu(xmm_save(i), as_XMMRegister(i));
}
}
const Address rdi_save(rbp, rdi_off * wordSize);
const Address rsi_save(rbp, rsi_off * wordSize);
__ movptr(rsi_save, rsi);
__ movptr(rdi_save, rdi);
#else
const Address mxcsr_save(rbp, mxcsr_off * wordSize);
{
Label skip_ldmx;
__ stmxcsr(mxcsr_save);
__ movl(rax, mxcsr_save);
__ andl(rax, 0xFFC0); // Mask out any pending exceptions (only check control and mask bits)
ExternalAddress mxcsr_std(StubRoutines::x86::addr_mxcsr_std());
__ cmp32(rax, mxcsr_std, rscratch1);
__ jcc(Assembler::equal, skip_ldmx);
__ ldmxcsr(mxcsr_std, rscratch1);
__ bind(skip_ldmx);
}
#endif
// Load up thread register
__ movptr(r15_thread, thread);
__ reinit_heapbase();
#ifdef ASSERT
// make sure we have no pending exceptions
{
Label L;
__ cmpptr(Address(r15_thread, Thread::pending_exception_offset()), NULL_WORD);
__ jcc(Assembler::equal, L);
__ stop("StubRoutines::call_stub: entered with pending exception");
__ bind(L);
}
#endif
// pass parameters if any
BLOCK_COMMENT("pass parameters if any");
Label parameters_done;
__ movl(c_rarg3, parameter_size);
__ testl(c_rarg3, c_rarg3);
__ jcc(Assembler::zero, parameters_done);
Label loop;
__ movptr(c_rarg2, parameters); // parameter pointer
__ movl(c_rarg1, c_rarg3); // parameter counter is in c_rarg1
__ BIND(loop);
__ movptr(rax, Address(c_rarg2, 0));// get parameter
__ addptr(c_rarg2, wordSize); // advance to next parameter
__ decrementl(c_rarg1); // decrement counter
__ push(rax); // pass parameter
__ jcc(Assembler::notZero, loop);
// call Java function
__ BIND(parameters_done);
__ movptr(rbx, method); // get Method*
__ movptr(c_rarg1, entry_point); // get entry_point
__ mov(r13, rsp); // set sender sp
BLOCK_COMMENT("call Java function");
__ call(c_rarg1);
BLOCK_COMMENT("call_stub_return_address:");
return_address = __ pc();
// store result depending on type (everything that is not
// T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT)
__ movptr(c_rarg0, result);
Label is_long, is_float, is_double, exit;
__ movl(c_rarg1, result_type);
__ cmpl(c_rarg1, T_OBJECT);
__ jcc(Assembler::equal, is_long);
__ cmpl(c_rarg1, T_LONG);
__ jcc(Assembler::equal, is_long);
__ cmpl(c_rarg1, T_FLOAT);
__ jcc(Assembler::equal, is_float);
__ cmpl(c_rarg1, T_DOUBLE);
__ jcc(Assembler::equal, is_double);
// handle T_INT case
__ movl(Address(c_rarg0, 0), rax);
__ BIND(exit);
// pop parameters
__ lea(rsp, rsp_after_call);
#ifdef ASSERT
// verify that threads correspond
{
Label L1, L2, L3;
__ cmpptr(r15_thread, thread);
__ jcc(Assembler::equal, L1);
__ stop("StubRoutines::call_stub: r15_thread is corrupted");
__ bind(L1);
__ get_thread(rbx);
__ cmpptr(r15_thread, thread);
__ jcc(Assembler::equal, L2);
__ stop("StubRoutines::call_stub: r15_thread is modified by call");
__ bind(L2);
__ cmpptr(r15_thread, rbx);
__ jcc(Assembler::equal, L3);
__ stop("StubRoutines::call_stub: threads must correspond");
__ bind(L3);
}
#endif
__ pop_cont_fastpath();
// restore regs belonging to calling function
#ifdef _WIN64
// emit the restores for xmm regs
if (VM_Version::supports_evex()) {
for (int i = xmm_save_first; i <= last_reg; i++) {
__ vinsertf32x4(as_XMMRegister(i), as_XMMRegister(i), xmm_save(i), 0);
}
} else {
for (int i = xmm_save_first; i <= last_reg; i++) {
__ movdqu(as_XMMRegister(i), xmm_save(i));
}
}
#endif
__ movptr(r15, r15_save);
__ movptr(r14, r14_save);
__ movptr(r13, r13_save);
__ movptr(r12, r12_save);
__ movptr(rbx, rbx_save);
#ifdef _WIN64
__ movptr(rdi, rdi_save);
__ movptr(rsi, rsi_save);
#else
__ ldmxcsr(mxcsr_save);
#endif
// restore rsp
__ addptr(rsp, -rsp_after_call_off * wordSize);
// return
__ vzeroupper();
__ pop(rbp);
__ ret(0);
// handle return types different from T_INT
__ BIND(is_long);
__ movq(Address(c_rarg0, 0), rax);
__ jmp(exit);
__ BIND(is_float);
__ movflt(Address(c_rarg0, 0), xmm0);
__ jmp(exit);
__ BIND(is_double);
__ movdbl(Address(c_rarg0, 0), xmm0);
__ jmp(exit);
return start;
}