当前位置: 首页>后端>正文

jvm (1) 主函数启动

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

https://www.xamrdz.com/backend/3f61940135.html

相关文章: